Python Django什么时候查找外键的主键?

Python Django什么时候查找外键的主键?,python,django,django-orm,Python,Django,Django Orm,我有两个简单的模型,一个代表一部电影,另一个代表一部电影的评级 class电影(models.Model): id=models.AutoField(主键=True) title=models.TextField() 等级评定(型号.型号): id=models.AutoField(主键=True) 电影=模型。外键(电影) rating=models.FloatField() 我的期望是,我将能够首先创建一部电影和一部引用该电影的评论,然后将它们都提交到数据库,只要我首先提交电影,以便为评论

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

class电影(models.Model):
id=models.AutoField(主键=True)
title=models.TextField()
等级评定(型号.型号):
id=models.AutoField(主键=True)
电影=模型。外键(电影)
rating=models.FloatField()
我的期望是,我将能够首先创建一部
电影
和一部引用该电影的
评论
,然后将它们都提交到数据库,只要我首先提交
电影
,以便为
评论
提供一个主键

the_hobbit = Movie(title="The Hobbit")
my_rating = Rating(movie=the_hobbit, rating=8.5)
the_hobbit.save()
my_rating.save()
令我惊讶的是,它仍然引发了一个
IntegrityError
抱怨我试图指定一个空外键,甚至
电影
已经提交,现在有了主键

IntegrityError:movie\u id列中的null值违反了not null约束
我通过添加一些
print
语句确认了这一点:

打印“the_hobbit.id=”,the_hobbit.id#无
打印“my_rating.movie.id=”,my_rating.movie.id#无
打印“my_rating.movie_id=”,my_rating.movie_id#无
霍比特人拯救
打印“the_hobbit.id=”,the_hobbit.id#3
打印“my_rating.movie.id=”,my_rating.movie.id#3
打印“my_rating.movie_id=”,my_rating.movie_id#无
我的_rating.save()#提高了IntegrityError
.movie
属性指的是一个
movie
实例,它确实有一个非
None
.id,但是
.movie\u id
保存在
movie
实例装箱时的值
None

当我试图提交
评论时,我希望Django会查找
.movie.id
,但显然这不是它所做的


在一边 在我的例子中,我通过在一些模型上重写
.save()
方法来处理这种行为,以便它们在保存之前再次查找外键的主键

def保存(自,*a,**kw):
对于self.\u meta.fields中的字段:
如果isinstance(字段,外键):
id_attname=field.attname
实例\u attname=id\u attname.r分区(“\u id”)[0]
instance=getattr(self,instance\u attname)
instance_id=instance.pk
setattr(self,id\u attname,instance\u id)
返回模式。保存(自,*a,**kw)
这是有问题的,但对我来说很有效,所以我并不是真的在寻找解决这个问题的方法


我在寻找对Django行为的解释。Django在什么点查找主键以查找外键?请具体说明;最好参考Django源代码。

如文档所述:

关键字参数仅仅是您所输入的字段的名称 在您的模型上定义。请注意,实例化一个模型是不可能的 触摸你的数据库;为此,需要保存()

在模型类上添加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")
在自定义管理器上添加方法(通常首选):

产地:

当您分配_hobbit时,您分配的是一个Movie实例,因此不会命中数据库。调用“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没有对您要求它存储在数据库中的对象进行任何动态更新的原因

从数据库重新加载对象可以为您提供一个设置了所有内容的完美对象,但它也会使您的变量指向不同的对象。因此,在您的示例中,如果您已经将指针指向“旧”电影对象,那么这将不会有帮助

赫德提到的
Movie.objects.create(title=“The Hobbit”)
就是这里的诀窍。它从数据库返回一个电影对象,因此它已经有了一个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时,我对对象和数据库中的对象之间的差异也有问题。我放在我的日志上的内容与上面的相同,但措辞有点不同。)

只是为了完成,因为我无法评论

您也可能(但在本例中不是这样)愿意更改数据库端的行为。这对于运行一些可能导致类似问题的测试可能很有用(因为它们是在提交和回滚过程中完成的)。有时,为了使测试尽可能接近应用程序的真实行为,最好使用此hacky命令,而不是将其打包在TransactionalTestCase中:

它与约束的属性有关。。。执行以下命令也将解决此问题(仅限PostgreSQL):


我的意见是,在你对你的霍比特人对象调用save()方法之后,这个对象就被保存了。但您的我的\u评级中存在的本地参考对象doe
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()
SET CONSTRAINTS [ALL / NAME] DEFERRABLE INITIALLY IMMEDIATE;
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))