django OneToOneField和ForeignKey有什么区别?
django OneToOneField和ForeignKey有什么区别?
要小心认识到OneToOneField(SomeModel)
和ForeignKey(SomeModel, unique=True)
之间有一些区别。 正如“Django权威指南”所述:
OneToOneField
一对一的关系。 从概念上讲,这与
ForeignKey
的unique=True
类似,但是关系的“反向”侧将直接返回单个对象。
与OneToOneField
“反向”关系相反, ForeignKey
“反向”关系返回一个QuerySet
。
例
例如,如果我们有以下两个模型(下面的完整模型代码):
-
Car
模型使用OneToOneField(Engine)
-
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')