Django何时查找外键的主键? [英] When does Django look up the primary key of foreign keys?

查看:211
本文介绍了Django何时查找外键的主键?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个简单的模型,一个代表一个电影,另一个代表电影的评级。

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

.movi​​e 属性指的是一个电影实例,它有一个非 .id ,但 .movi​​e_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查找 .movi​​e.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.movi​​e to the_hobbit 。但是, self.movi​​e 不是常规属性,而是通过 __ 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆