У меня есть две простые модели, одна из которых представляет фильм, другая представляет рейтинг для фильма.
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.