Django何时查找外键的主键? [英] When does Django look up the primary key of foreign keys?
问题描述
我有两个简单的模型,一个代表一个电影,另一个代表电影的评级。
I have two simple models, one representing a movie an the other representing a rating for a movie.
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
首先,它被赋予评论
引用的主键。
My expectation is that I would be able to first create a Movie
and a Review
referencing that movie then commit them both to the database, as long as I committed the Movie
first so that it was given a primary key for the Review
to refer to.
the_hobbit = Movie(title="The Hobbit")
my_rating = Rating(movie=the_hobbit, rating=8.5)
the_hobbit.save()
my_rating.save()
令我惊喜它仍然提出了一个 IntegrityError
抱怨我试图指定一个空的外键,即使 Movie
已经提交,现在有一个主键。
To my surprise it still raised an IntegrityError
complaining that I was trying to specify a null foreign key, even the Movie
had been committed and now had a primary key.
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
属性指的是一个电影
实例,它有一个非无
.id
,但 .movie_id
持有值无
它是当电影
实例被装箱时。
The .movie
attribute is referring to a Movie
instance which does have a non-None
.id
, but .movie_id
is holding into the value None
that it had when the Movie
instance was crated.
我希望Django查找 .movie.id
当我尝试提交评论
,但显然不是这样做。
I expected Django to look up .movie.id
when I tried to commit the Review
, but apparently that's not what it's doing.
在我的情况下,我已经处理了这个行为在某些型号上覆盖 .save()
方法,以便在保存之前再次查找外键的主键。
In my case, I've dealt this this behaviour by overriding the .save()
method on some models so that they look up the primary keys of foreign keys again before saving.
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)
这是黑客,但它适用于我,所以我不是真的寻找一个解决方案这个特殊问题。
This is hacky, but it works for me so I am not really looking for a solution to this particular problem.
我正在寻找Django行为的解释。 Django在什么地方查找外键的主键?请具体说明引用Django源代码将是最好的。
I am looking for an explanation of Django's behaviour. At what points does Django look up the primary key for foreign keys? Please be specific; references to the Django source code would be best.
推荐答案
查看 Django源,答案在于Django的一些魔术用来提供它的好API。
Looking in the Django source, the answer lies in some of the magic Django uses to provide its nice API.
当您实例化一个评级
对象时,Django设置(尽管有更多的间接使此通用) self.movie
to the_hobbit
。但是, self.movie
不是常规属性,而是通过 __ set __
设置。 __ set __
方法(上面链接)查看值( the_hobbit
),并尝试设置属性 movie_id
而不是电影
,因为它是一个 ForeignKey
字段。但是,由于 the_hobbit.pk
为None,所以将电影
设置为 the_hobbit
一旦您尝试保存您的评分,它会尝试再次查找 movie_id
,该失败(甚至没有尝试看电影
。)
When you instantiate a Rating
object, Django sets (though with some more indirection to make this generic) self.movie
to the_hobbit
. However, self.movie
isn't a regular property, but is rather set through __set__
. The __set__
method (linked above) looks at the value (the_hobbit
) and tries to set the property movie_id
instead of movie
, since it's a ForeignKey
field. However, since the_hobbit.pk
is None, it just sets movie
to the_hobbit
instead. Once you try to save your rating, it tries to look up movie_id
again, which fails (it doesn't even try to look at movie
.)
有趣的是,在 Django 1.5 。
setattr(value, self.related.field.attname, getattr(
instance, self.related.field.rel.get_related_field().attname))
# "self.movie_id = movie.pk"
它现在是
related_pk = getattr(instance, self.related.field.rel.get_related_field().attname)
if related_pk is None:
raise ValueError('Cannot assign "%r": "%s" instance isn\'t saved in the database.' %
(value, instance._meta.object_name))
在你的情况下导致更有用的错误消息。
which in your case would result in a more helpful error message.
这篇关于Django何时查找外键的主键?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!