Django何时查询外键的主键?

2024-06-01 08:13:07 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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指定一个主键。在

^{pr2}$

令我惊讶的是,它仍然引发了一个IntegrityError抱怨我试图指定一个空外键,甚至{}已经提交,现在有一个主键。在

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,但是{}保留了None实例装箱时的值{}。在

当我试图提交Review时,我希望Django查找.movie.id,但显然它并不是这样做的。在


一边

在我的例子中,我通过重写一些模型上的.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源代码。在


Tags: theinstanceselfnoneidmodelsmysave
3条回答

如文件所述:

The keyword arguments are simply the names of the fields you’ve defined on your model. Note that instantiating a model in no way touches your database; for that, you need to save().

在模型类上添加classmethod:

class Book(models.Model):
    title = models.CharField(max_length=100)

    @classmethod
    def create(cls, title):
        book = cls(title=title)
        # do something with the book
        return book

book = Book.create("Pride and Prejudice")

在自定义管理器上添加方法(通常首选):

^{pr2}$

来源:https://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs#creating-objects

当你指定哈比人时,你是在指定一个电影的实例,因此不会触及数据库。调用“save”后,数据库确实会填满,但是您的变量仍然指向内存中的对象,没有意识到数据库的突然更改。在

也就是说,改变序列的顺序也可以有效地创建对象:

the_hobbit = Movie(title="The Hobbit")
the_hobbit.save()
my_rating = Rating(movie=the_hobbit, rating=8.5)
my_rating.save()

主要问题与副作用有关,这些副作用是否需要。在Python中,变量实际上是指向对象的指针。在

当您从模型中创建对象时,它还没有主键,因为您尚未保存它。但是,在保存它时,Django应该确保它更新已经存在的对象的属性吗?主键是合乎逻辑的,但它也会导致其他属性被更新。在

Django的unicode处理就是一个例子。无论你给你放入数据库的文本赋予什么字符集:一旦你再次取出文本,Django就会给你unicode。但是,如果您创建一个对象(带有一些非unicode属性)并保存它,Django是否应该修改现有对象上的文本属性?这听起来已经有点危险了。这可能就是为什么Django不执行任何您要求它存储在数据库中的对象的动态更新。在

从数据库重新加载对象会给你一个完美的对象,所有的东西都设置好了,但它也会使你的变量指向另一个对象。所以在你的例子中,如果你已经给评级一个指针指向你的“旧”电影对象,那就没有帮助了。在

Hedde提到的Movie.objects.create(title="The Hobbit")就是这里的诀窍。它从数据库返回一个movie对象,因此它已经有了一个id

the_hobbit = Movie.objects.create(title="The Hobbit")
my_rating = Rating(movie=the_hobbit, rating=8.5)
# No need to save the_hobbit, btw, it is already saved.
my_rating.save()

(当我新创建的对象没有输出unicode时,我的对象和数据库中的对象之间的差异也有问题。我在我的博客上放的explanation和上面一样,但措辞有点不同。)

Django source中,答案在于Django使用了一些魔术来提供它的优秀API。在

当您实例化一个Rating对象时,Django会将self.movie设置为the_hobbit(尽管使用了一些更间接的方法使其通用)。但是,self.movie不是常规属性,而是通过__set__设置的。__set__方法(上面链接)查看值(the_hobbit),并尝试设置属性movie_id,而不是{},因为它是一个ForeignKey字段。但是,由于the_hobbit.pk为None,它只将movie设置为the_hobbit。一旦您试图保存评分,它会再次尝试查找movie_id,但失败了(它甚至不尝试查看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"

现在是了

^{pr2}$

在您的情况下,这将导致一个更有用的错误消息。在

相关问题 更多 >