django OneToOneField和ForeignKey有什么区别?

What's the difference between django OneToOneField and ForeignKey?

Django Onetoonefield和Foreignkey有什么区别?


请注意,OneToOneField(SomeModel)ForeignKey(SomeModel, unique=True)之间存在一些差异。如《Django最终指南》所述:

OneToOneField

A one-to-one relationship. Conceptually, this is similar to a ForeignKey with unique=True, but the"reverse" side of the relation will directly return a single object.

OneToOneField的"反向"关系不同,ForeignKey的"反向"关系返回QuerySet

例子

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

  • Car型采用OneToOneField(Engine)
  • Car2型采用ForeignKey(Engine2, unique=True)
  • python manage.py shell内执行以下操作:

    OneToOneField示例

    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>

    ForeignKeyunique=True示例

    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 ForeignKey

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

    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

    运行python manage.py syncdb以执行SQL代码并在数据库中为应用程序构建表。然后使用python manage.py shell打开一个python shell。

    创建报告器对象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 ForeignKey用于定义多对一关系。

    OneToOneField用于创建一对一关系。

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

    每次你想发表一篇新的文章,你都必须创建一个新的报告对象。这很费时,不是吗?

    我强烈建议用OneToOneField来尝试这个例子,并认识到不同之处。我敢肯定,在这个例子之后,您将完全了解django OneToOneField和django ForeignKey之间的区别。


    OneTooneField(一对一)实现了面向对象的合成概念,而ForeignKey(一对多)则与聚合有关。


    另外,OneToOneField还可以用作主密钥,以避免密钥重复。可能没有隐式/显式自动域

    1
    models.AutoField(primary_key=True)

    但是使用OneToOneField作为主键(例如,假设UserProfile模型):

    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值相对应的多个记录。