What's the difference between django OneToOneField and ForeignKey?
Django Onetoonefield和Foreignkey有什么区别?
请注意,
OneToOneField
A one-to-one relationship. Conceptually, this is similar to a
ForeignKey withunique=True , but the"reverse" side of the relation will directly return a single object.
号
与
例如,如果我们有以下两个模型(下面是完整的模型代码):
在
1 2 3 4 5 | >>> from testapp.models import Car, Engine >>> c = Car.objects.get(name='Audi') >>> e = Engine.objects.get(name='Diesel') >>> e.car <Car: Audi> |
1 2 3 4 5 | >>> from testapp.models import Car2, Engine2 >>> c2 = Car2.objects.get(name='Mazda') >>> e2 = Engine2.objects.get(name='Wankel') >>> e2.car2_set.all() [<Car2: Mazda>] |
号型号代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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, on_delete=models.CASCADE) def __unicode__(self): return self.name |
Foreignkey是一对多的,所以一个汽车对象可能有很多个轮子,每个轮子都有一个它所属汽车的Foreignkey。一个OneTooneField就像一个引擎,一个汽车对象只能有一个引擎。
学习新事物的最好和最有效的方法是看和学习现实世界中的实例。假设你想在Django建立一个博客,在那里记者可以撰写和发表新闻文章。这家在线报纸的老板希望允许他的每一位记者发表他们想要的文章,但不希望不同的记者在同一篇文章上工作。这意味着当读者去阅读一篇文章时,他们只会在文章中看到一位作者。
例如:约翰的文章,哈里的文章,里克的文章。因为老板不希望两个或两个以上的作者在同一篇文章上工作,所以您不能拥有Harry&Rick的文章。
在姜戈的帮助下,我们如何解决这个"问题"?解决这个问题的关键是django
下面是完整的代码,可以用来实现老板的想法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
运行
创建报告器对象R1。
1 2 3 4 5 | In [49]: from thepub.models import Reporter, Article In [50]: R1 = Reporter(first_name='Rick') In [51]: R1.save() |
号
创建项目对象A1。
1 2 3 | In [5]: A1 = Article.objects.create(title='TDD In Django', reporter=R1) In [6]: A1.save() |
然后使用下面的代码获取报告者的姓名。
1 2 | In [8]: A1.reporter.first_name Out[8]: 'Rick' |
。
现在,通过运行以下python代码来创建报告器对象r2。
1 2 3 | In [9]: R2 = Reporter.objects.create(first_name='Harry') In [10]: R2.save() |
现在尝试将r2添加到项目对象a1中。
1 | In [13]: A1.reporter.add(R2) |
。
它不起作用,您将得到一个attributeError,它说"reporter"对象没有"add"属性。
正如您所看到的,一个文章对象不能与多个报告者对象相关。
R1呢?我们可以给它附加多个文章对象吗?
1 2 3 4 | 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
我们可以在上面的models.py文件中使用
每次你想发表一篇新的文章,你都必须创建一个新的报告对象。这很费时,不是吗?
我强烈建议用
OneTooneField(一对一)实现了面向对象的合成概念,而ForeignKey(一对多)则与聚合有关。
另外,
1 | models.AutoField(primary_key=True) |
但是使用
1 2 | user = models.OneToOneField( User, null=False, primary_key=True, verbose_name='Member profile') |
。
当您访问一个OneToonField时,您将获得所查询字段的值。在本例中,图书模型的"标题"字段是一个OneTooneField:
1 2 3 4 | >>> from mysite.books.models import Book >>> b = Book.objects.get(id=50) >>> b.title u'The Django Book' |
。
当您访问一个foreignkey时,您会得到相关的模型对象,然后您可以对其执行进一步的查询。在本例中,同一个图书模型的"publisher"字段是一个foreignkey(与publisher类模型定义相关):
1 2 3 4 5 | >>> b = Book.objects.get(id=50) >>> b.publisher <Publisher: Apress Publishing> >>> b.publisher.website u'http://www.apress.com/' |
对于foreignkey字段,查询也可以用另一种方式工作,但由于关系的非对称性,它们略有不同。
1 2 3 | >>> p = Publisher.objects.get(name='Apress Publishing') >>> p.book_set.all() [<Book: The Django Book>, <Book: Dive Into Python>, ...] |
。
在幕后,Book_集只是一个查询集,可以像其他任何查询集一样进行过滤和切片。属性名簿集是通过将小写模型名附加到集而生成的。
OneTooneField:如果第二个表与
1 | table2_col1 = models.OneToOneField(table1,on_delete=models.CASCADE, related_name='table1_id') |
表2将只包含与表1的pk值相对应的一条记录,即表2_col1的唯一值等于表的pk。
1 | table2_col1 == models.ForeignKey(table1, on_delete=models.CASCADE, related_name='table1_id') |
。
表2可能包含与表1的pk值相对应的多个记录。