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 об автополях модели сказано следующее:
Вот соответствующие определения моделей
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
.
Надеюсь, это поможет вам.