У меня есть две простые модели, одна из которых представляет фильм, другая представляет рейтинг для фильма.
class Movie(models.Model):
id = models.AutoField(primary_key=True)
title = models.TextField()
class Rating(models.Model):
id = models.AutoField(primary_key=True)
movie = models.ForeignKey(Movie)
rating = models.FloatField()
Мое предположение заключается в том, что я мог бы сначала создать ссылки Movie и Review, ссылающиеся на этот фильм, и передать их обе в базу данных, если я сначала совершил Movie, чтобы мне дали первичный ключ для Review для ссылки.
the_hobbit = Movie(title="The Hobbit")
my_rating = Rating(movie=the_hobbit, rating=8.5)
the_hobbit.save()
my_rating.save()
К моему удивлению, он все же поднял сообщение IntegrityError, жалуясь на то, что я пытался указать нулевой внешний ключ, даже Movie был зафиксирован, и теперь у него был первичный ключ.
IntegrityError: null value in column "movie_id" violates not-null constraint
Я подтвердил это, добавив несколько операторов print:
print "the_hobbit.id =", the_hobbit.id # None
print "my_rating.movie.id =", my_rating.movie.id # None
print "my_rating.movie_id =", my_rating.movie_id # None
the_hobbit.save()
print "the_hobbit.id =", the_hobbit.id # 3
print "my_rating.movie.id =", my_rating.movie.id # 3
print "my_rating.movie_id =", my_rating.movie_id # None
my_rating.save() # raises IntegrityError
Атрибут .movie ссылается на экземпляр Movie, который имеет не None .id, но .movie_id удерживает значение None, которое оно имело, когда экземпляр Movie был упакован.
Я ожидал, что Django будет искать .movie.id, когда я попытаюсь зафиксировать Review, но, видимо, это не то, что он делает.
Помимо
В моем случае я рассмотрел это поведение, переопределив метод .save() на некоторых моделях, чтобы они снова искали первичные ключи внешних ключей перед сохранением.
def save(self, *a, **kw):
for field in self._meta.fields:
if isinstance(field, ForeignKey):
id_attname = field.attname
instance_attname = id_attname.rpartition("_id")[0]
instance = getattr(self, instance_attname)
instance_id = instance.pk
setattr(self, id_attname, instance_id)
return Model.save(self, *a, **kw)
Это взломанный, но он работает для меня, поэтому я действительно не ищу решение этой конкретной проблемы.
Я ищу объяснение поведения Django. В каких точках Django просматривает первичный ключ для внешних ключей? Пожалуйста, будьте конкретны; лучше всего использовать ссылки на исходный код Django.