django OneToOneField和ForeignKey有什么区别?

django OneToOneField和ForeignKey有什么区别?

要小心认识到OneToOneField(SomeModel)ForeignKey(SomeModel, unique=True)之间有一些区别。 正如“Django权威指南”所述:

OneToOneField

一对一的关系。 从概念上讲,这与ForeignKeyunique=True类似,但是关系的“反向”侧将直接返回单个对象。

OneToOneField “反向”关系相反, ForeignKey “反向”关系返回一个QuerySet

例如,如果我们有以下两个模型(下面的完整模型代码):

  1. Car模型使用OneToOneField(Engine)
  2. Car2模型使用ForeignKey(Engine2, unique=True)

python manage.py shell执行以下命令:

OneToOneField示例

 >>> from testapp.models import Car, Engine >>> c = Car.objects.get(name='Audi') >>> e = Engine.objects.get(name='Diesel') >>> e.car <Car: Audi> 

具有unique=True示例的ForeignKey

 >>> from testapp.models import Car2, Engine2 >>> c2 = Car2.objects.get(name='Mazda') >>> e2 = Engine2.objects.get(name='Wankel') >>> e2.car2_set.all() [<Car2: Mazda>] 

型号代码

 from django.db import models class Engine(models.Model): name = models.CharField(max_length=25) def __unicode__(self): return self.name class Car(models.Model): name = models.CharField(max_length=25) engine = models.OneToOneField(Engine) def __unicode__(self): return self.name class Engine2(models.Model): name = models.CharField(max_length=25) def __unicode__(self): return self.name class Car2(models.Model): name = models.CharField(max_length=25) engine = models.ForeignKey(Engine2, unique=True) def __unicode__(self): return self.name 

一个ForeignKey是一对多的,所以一个Car对象可能有多个Wheels,每个Wheel都有一个到它所属的Car的ForeignKey。 OneToOneField就像一个引擎,一个Car对象可以只有一个。

学习新事物最好也是最有效的方法就是看现实世界的实例。 假设你想在django上build立一个记者可以写和发表新闻文章的博客。 网上报纸的老板想让每个记者发表他们想要的尽可能多的文章,但不希望不同的记者在同一篇文章上工作。 这意味着当读者阅读一篇文章时,他们只会在文章中select一个作者。

例如:John的文章,Harry的文章,Rick的文章。 Harry&Rick不能拥有Article,因为老板不希望两个或两个以上的作者在同一篇文章上工作。

如何在django的帮助下解决这个“问题”? 解决这个问题的关键是django的ForeignKey

以下是可以用来实现我们老板想法的完整代码。

 from django.db import models # Create your models here. class Reporter(models.Model): first_name = models.CharField(max_length=30) def __unicode__(self): return self.first_name class Article(models.Model): title = models.CharField(max_length=100) reporter = models.ForeignKey(Reporter) def __unicode__(self): return self.title 

运行python manage.py syncdb来执行sql代码,并在您的数据库中为您的应用程序构build表。 然后使用python manage.py shell打开一个python shell。

创buildReporter对象R1。

 In [49]: from thepub.models import Reporter, Article In [50]: R1 = Reporter(first_name='Rick') In [51]: R1.save() 

创build文章对象A1。

 In [5]: A1 = Article.objects.create(title='TDD In Django', reporter=R1) In [6]: A1.save() 

然后使用下面的一段代码来获取记者的名字。

 In [8]: A1.reporter.first_name Out[8]: 'Rick' 

现在通过运行下面的python代码来创buildReporter对象R2。

 In [9]: R2 = Reporter.objects.create(first_name='Harry') In [10]: R2.save() 

现在尝试将R2添加到Article对象A1。

 In [13]: A1.reporter.add(R2) 

它不起作用,你会得到一个AttributeError说'记者'对象没有属性'添加'。

正如您所看到的,Article对象不能与多个Reporter对象相关联。

R1呢? 我们可以附加多个Article对象吗?

 In [14]: A2 = Article.objects.create(title='Python News', reporter=R1) In [15]: R1.article_set.all() Out[15]: [<Article: Python News>, <Article: TDD In Django>] 

这个实例告诉我们,django ForeignKey用来定义多对一的关系。

OneToOneField用于创build一对一的关系。

我们可以在上面的models.py文件中使用reporter = models.OneToOneField(Reporter) ,但是在我们的例子中,这不会有用,因为作者将不能发布多篇文章。

每次你想发布一篇新文章,你将不得不创build一个新的Reporter对象。 这很费时,不是吗?

我强烈build议用OneToOneField来尝试一下这个例子,并认识它们之间的差异。 我很确定,在这个例子之后,你将完全知道django OneToOneField和django ForeignKey的区别。

OneToOneField(一对一)以面向对象的方式实现组合的概念,而ForeignKey(一对多)涉及到聚合。

当你访问OneToOneField时,你会得到你查询的字段的值。 在这个例子中,书籍模型的“标题”字段是OneToOneField:

 >>> from mysite.books.models import Book >>> b = Book.objects.get(id=50) >>> b.title u'The Django Book' 

当你访问一个ForeignKey的时候,你会得到相关的模型对象,然后你可以进行进一步的查询。 在这个示例中,相同的书籍模型的“发布者”字段是ForeignKey(与发布者类模型定义相关):

 >>> b = Book.objects.get(id=50) >>> b.publisher <Publisher: Apress Publishing> >>> b.publisher.website u'http://www.apress.com/' 

使用ForeignKey字段,查询也是以另一种方式工作的,但是由于关系的非对称特性,它们有些不同。

 >>> p = Publisher.objects.get(name='Apress Publishing') >>> p.book_set.all() [<Book: The Django Book>, <Book: Dive Into Python>, ...] 

在幕后,book_set只是一个QuerySet,可以像其他任何QuerySet一样进行过滤和切片。 属性名称book_set通过将小写模型名称附加到_set来生成。

OneToOneField也可以用作主键来避免密钥重复。 一个人可能没有隐式/显式自动字段

 models.AutoField(primary_key=True) 

但使用OneToOneField作为主键代替(例如,想象UserProfile模型):

 user = models.OneToOneField( User, null=False, primary_key=True, verbose_name='Member profile')