我有两个简单的模型,一个代表一部电影,另一个代表一部电影的评级。在
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
指定一个主键。在
令我惊讶的是,它仍然引发了一个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源代码。在
如文件所述:
在模型类上添加classmethod:
在自定义管理器上添加方法(通常首选):
^{pr2}$来源:https://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs#creating-objects
当你指定哈比人时,你是在指定一个电影的实例,因此不会触及数据库。调用“save”后,数据库确实会填满,但是您的变量仍然指向内存中的对象,没有意识到数据库的突然更改。在
也就是说,改变序列的顺序也可以有效地创建对象:
主要问题与副作用有关,这些副作用是否需要。在Python中,变量实际上是指向对象的指针。在
当您从模型中创建对象时,它还没有主键,因为您尚未保存它。但是,在保存它时,Django应该确保它更新已经存在的对象的属性吗?主键是合乎逻辑的,但它也会导致其他属性被更新。在
Django的unicode处理就是一个例子。无论你给你放入数据库的文本赋予什么字符集:一旦你再次取出文本,Django就会给你unicode。但是,如果您创建一个对象(带有一些非unicode属性)并保存它,Django是否应该修改现有对象上的文本属性?这听起来已经有点危险了。这可能就是为什么Django不执行任何您要求它存储在数据库中的对象的动态更新。在
从数据库重新加载对象会给你一个完美的对象,所有的东西都设置好了,但它也会使你的变量指向另一个对象。所以在你的例子中,如果你已经给评级一个指针指向你的“旧”电影对象,那就没有帮助了。在
Hedde提到的
Movie.objects.create(title="The Hobbit")
就是这里的诀窍。它从数据库返回一个movie对象,因此它已经有了一个id(当我新创建的对象没有输出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中发生变化。在
而不是
现在是了
^{pr2}$在您的情况下,这将导致一个更有用的错误消息。在
相关问题 更多 >
编程相关推荐