Django автоматически добавляет поле ID во время модульных тестов, несмотря на то, что я объявил первичный ключ для своей модели

У меня есть проект django с несколькими таблицами, и я отвечаю за написание юнит-тестов для api этого проекта django. Одна из таблиц имеет ForeignKey другой таблицы, однако, когда я пытаюсь создать объект модели в юнит-тесте, я получаю следующую IntegrityError после того, как объект модели, на который ссылается ForeignKey, успешно запрошен, несмотря на то, что я могу размещать данные в этой таблице во время работы сервера:

Traceback (most recent call last):
  File "C:\Python312\Lib\site-packages\django\db\backends\utils.py", line 105, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\site-packages\django\db\backends\sqlite3\base.py", line 328, in execute
    return super().execute(query, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.IntegrityError: NOT NULL constraint failed: app_examplemodel.location_id

The above exception was the direct cause of the following exception:

...

Traceback (most recent call last):
  File "~\tests\test_example_model.py", line 19, in test_post_example_model_valid       
    response = self.client.post(reverse("examplemodelurl"), EXAMPLE_MODEL_DICT, content_type="application/json")

django.db.utils.IntegrityError: NOT NULL constraint failed: app_examplemodel.location_id

Вот юнит-тест (он выкидывает ошибку до того, как я успеваю что-то утвердить) :

class TestPostExampleModel(TestCase):
    def test_post_example_model_valid(self):
        self.client.post(reverse("locations"), {"name": "location name"}, content_type="application/json")
        response = self.client.post(reverse("examplemodelurl"), {"name": "example name", "location": "location name"}, content_type="application/json")

Главное, что смущает в этом вопросе, это то, что на официальном сайте django об автополях модели сказано следующее:

Если вы хотите указать пользовательский первичный ключ, укажите primary_key=True для одного из ваших полей. Если Django увидит, что вы явно задали Field.primary_key, он не будет добавлять автоматический столбец id.

Вот соответствующие определения моделей

from django.db import models

class Location(models.Model):
    name = models.CharField(primary_key=True, max_length=64, unique=True)
    location_type = models.CharField(default='other', max_length=10)
    address = models.CharField(max_length=128, blank=True)

    def __str__(self) -> str:
        return f'{self.name}'

class BaseExampleModel(models.Model):
    name = models.CharField(max_length=63, primary_key=True, unique=True)

    def __str__(self):
        return self.name
    
    class Meta:
        abstract = True

ExampleModel(BaseExampleModel):
    location = models.ForeignKey(Location, on_delete=models.PROTECT)

А вот сериализаторы, которые я использую для проверки этих моделей:

from rest_framework import serializers
from app.models import *

class LocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = "__all__"

class ExampleModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = ExampleModel
        fields = "__all__"
        depth = 1

Если верить документации django, поле ID не должно создаваться для модели местоположения, однако ограничение NOT NULL не работает для app_examplemodel.location_id после завершения запроса на получение объекта местоположения. Ошибка исчезает, если я помещаю null=True в определение внешнего ключа, но вместо того, чтобы позволить значению быть нулевым, оно просто всегда оказывается нулевым. У меня есть и другие модели с внешними ключами в этом проекте, и они ведут себя так, как ожидалось.

Я предполагаю, что это как-то связано с тем, что examplemodel является дочерней моделью абстрактной модели, но это не объясняет, почему я могу успешно размещать данные в этой модели, когда сервер работает, но не во время unittests. Мой рассудок медленно истощается, я не смог найти ни одного сообщения на форуме или документацию, объясняющую, почему это происходит, и мне бы очень хотелось сейчас заниматься чем-нибудь другим.

пожалуйста, пришлите помощь

Учитывая следующий код:

ExampleModel(BaseExampleModel):
    location = models.ForeignKey(Location, on_delete=models.PROTECT)

Вы должны указать фактический ForeignKey объект Location при вызове post.

Поле location_id - это поле, созданное Django в вашем объекте модели Django. Оно содержит ссылку на фактический id объект Location, который вы назначили.

Правильный способ создания объекта ExampleModel заключается в следующем:

location = Location.objects.create(name='foo', adress='foo')
example_model = ExampleModel.objects.create(name='bar', location=location)

В вашем тесте это будет выглядеть примерно так

class TestPostExampleModel(TestCase):
    def test_post_example_model_valid(self):

        # You need to have a location object to create an examplemodel
        location = Location.objects.create(name='foo', adress='foo')
  
        # Here you see "location": location.id
        response = self.client.post(reverse("examplemodelurl"), {"name": "example name", "location": location.id}, content_type="application/json")

Это работает, когда вы задаете null=True, потому что null=True позволяет location быть null. Вы все еще можете редактировать местоположение, присвоив ему объект location и вызвав .save() на объекте ExampleModel.

Надеюсь, это поможет вам.

Вернуться на верх