django OneToOneField 和 ForeignKey 有啥区别?

Posted

技术标签:

【中文标题】django OneToOneField 和 ForeignKey 有啥区别?【英文标题】:What's the difference between django OneToOneField and ForeignKey?django OneToOneField 和 ForeignKey 有什么区别? 【发布时间】:2011-08-17 18:02:27 【问题描述】:

Django OneToOneFieldForeignKey 有什么区别?

【问题讨论】:

【参考方案1】:

OneToOneField(SomeModel)ForeignKey(SomeModel, unique=True) 之间的差异如 The Definitive Guide to Django 所述:

OneToOneField

一对一的关系。从概念上讲,这类似于 ForeignKeyunique=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>

ForeignKeyunique=True 示例

>>> 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, on_delete=models.CASCADE)

    def __unicode__(self):
        return self.name

【讨论】:

@MarkPneyer:据我了解,OneToOne 字段就是:一对一。它不必在。见this example:地方不一定是餐厅。 这个答案说“有一些差异”,然后说出一个差异。还有其他人吗? 我想知道和克里斯一样。是否只是语法糖,数据访问方式是否存在一些潜在差异,从而导致性能差异? 那么......什么时候甚至想要使用ForeignKeyunique=True 而不是OneToOneField?我在其他问题中看到 Django 甚至警告说 OneToOneField 通常最符合自己的利益。反向 QuerySet 永远不会有多个元素,对吧? 根据您的描述,OneToOne 和 ForeignKeyFields 在功能上似乎完全相同,因为它们可用于在任何情况下完成完全相同的任务,但事实并非如此。两者在功能上的重要区别在于,来自不同对象的许多外键可能映射到单个对象,而在 OneToOne 中,多个对象映射到单个对象是非法的。这个答案完全忽略了这一点,它确实是您选择使用哪一个时需要了解的唯一重要的事情......如果您设置 unique=true 它们在功能上是相同的。【参考方案2】:

我也对这两个字段的用法感到困惑。 让我举个例子来了解它们的用法,因为我最近遇到了这个问题并意识到了这两个字段的用法。

我有一个模型,像这样-

from django.contrib.auth.models import User
from django.db import models


class Attendance(models.Model):
     user = models.OneToOneField(User, on_delete=models.CASCADE, default="", null=True)
     date = models.CharField(max_length=11)

     def __int__(self):
         return self.id

现在的问题是我不能用同一个用户制作多个对象, 即同一用户将在多天出席。因此,具有相同用户的多个对象。

但 OneToOne 字段不允许我这样做。 Image for reference

所以,我将模型更改为-

from django.contrib.auth.models import User
from django.db import models


class Attendance(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, default="", null=True)
    date = models.CharField(max_length=11)

    def __int__(self):
        return self.id

现在它工作正常,我可以标记用户多天的出勤情况。

这就是区别所在,OneToOne 字段不允许您使用同一个用户(例如)创建多个对象,但使用 ForeignKey 是可能的。

【讨论】:

【参考方案3】:

ForeignKey 是多对一关系。因此,Car 对象可能有许多Wheel 实例。因此,每个Wheel 都会有一个ForeignKey 到它所属的CarOneToOneField 就像Engine 的一个实例,其中Car 对象可以有一个且只有一个。

【讨论】:

谢谢,Dose OneToOneField(someModel) mean ForeignKey(SomeModel, unique=True)? Yes: 'OneToOneField 本质上与 ForeignKey 相同,不同之处在于它始终带有“唯一”约束,并且反向关系始终返回指向的对象(因为只会有成为一个),而不是返回一个列表。' 几辆车有相同的引擎呢? @OlegTikhonov 他们可能拥有相同引擎设计的副本,但我希望看到多辆汽车共享相同物理引擎的实例。 这个答案中的术语有点混乱。根据官方 django 文档,ForeignKey 不是一对多,而是多对一关系:docs.djangoproject.com/en/2.0/ref/models/fields/…【参考方案4】:

OneToOneField(例如:一辆车有一个车主) ForeignKey(OneToMany) (例如:一家餐厅有很多项目)

【讨论】:

OneToOneField(例如:一家餐厅有一件商品)。 ForeignKey(例如:一家餐厅有很多项目)。 ManyToManyField(例如:许多麦当劳有许多相同的商品)。麦当劳有 Happy Meal、巨无霸等。巨无霸在许多不同的麦当劳餐厅中。【参考方案5】:

绘制项目之间关系的最简单方法是用通俗易懂的语言理解它们。示例

一个用户可以拥有多辆汽车,但一辆汽车只能拥有一个车主。建立这个之后,外键应该用在具有多关系的项目上。在这种情况下,汽车。这意味着您将在汽车中包含用户作为外键

一对一的关系非常简单。说一个人和一颗心。一个人只有一颗心,一颗心只能属于一个人

【讨论】:

那么你在哪里设置人类心脏示例的 OneToOne 字段?您是在 Hearth 类中设置 OneToOne 字段指向 Human 模型,还是在 Human 模型中设置 OneToOne 字段发布到 Heart 模型?如果我错了请告诉我,但我想最好将 OneToOne 字段放在 Heart 模型中并将其设置为主键对吗? @PaulBénéteau 您在子模型中设置了密钥...心脏模型无法独立存在...因此它成为用户模型的子模型。所以是的,你是对的【参考方案6】:

ForeignKey 允许您接收子类,它是另一个类的定义,但 OneToOneFields 不能这样做并且它不能附加到多个变量

【讨论】:

【参考方案7】:

OneToOneField:如果第二个表与

相关
table2_col1 = models.OneToOneField(table1,on_delete=models.CASCADE, related_name='table1_id')

table2 将只包含与 table1 的 pk 值对应的一条记录,即 table2_col1 将具有等于 table pk 的唯一值

table2_col1 == models.ForeignKey(table1, on_delete=models.CASCADE, related_name='table1_id')

table2可能包含多个对应table1的pk值的记录。

【讨论】:

【参考方案8】:

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

models.AutoField(primary_key=True)

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

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

【讨论】:

【参考方案9】:

学习新事物的最佳和最有效的方法是查看和研究现实世界的实际示例。假设您想在 django 中建立一个博客,记者可以在其中撰写和发布新闻文章。在线报纸的所有者希望允许他的每个记者发布任意数量的文章,但不希望不同的记者在同一篇文章上工作。这意味着当读者阅读一篇文章时,他们只会在文章中看到一位作者。

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

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

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

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。

创建 Reporter 对象 R1。

In [49]: from thepub.models import Reporter, Article

In [50]: R1 = Reporter(first_name='Rick')

In [51]: R1.save()

创建文章对象 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 代码创建 Reporter 对象 R2。

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

In [10]: R2.save()

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

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

它不起作用,你会得到一个 AttributeError 说 'Reporter' 对象没有属性 'add'。

如您所见,一个 Article 对象不能与多个 Reporter 对象相关。

R1 呢?我们可以将多个文章对象附加到它吗?

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),但在我们的示例中它不会有用,因为作者不能发布超过一篇文章。

每次您想发布一篇新文章时,您都必须创建一个新的 Reporter 对象。这很耗时,不是吗?

我强烈建议尝试使用 OneToOneField 的示例并了解其中的区别。我很确定在这个例子之后你会完全知道 django OneToOneField 和 django ForeignKey 之间的区别。

【讨论】:

我喜欢这个。 OneToOne 和 ForeignKey 的根本区别是一对一和一对多的关系。您可以使用 ForeignKey 和 unique=True 进行一对一,细微的区别在 Matthew 的回复中说明。【参考方案10】:

当您访问 OneToOneField 时,您将获得所查询字段的值。在此示例中,图书模型的“标题”字段是 OneToOneField:

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

当您访问 ForeignKey 时,您将获得相关的模型对象,然后您可以对其执行进一步的查询。在此示例中,同一图书模型的“publisher”字段是 ForeignKey(与 Publisher 类模型定义相关):

>>> 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 来生成的。

【讨论】:

【参考方案11】:

OneToOneField(一对一)在面向对象中实现了组合的概念,而 ForeignKey(一对多)与聚合相关。

【讨论】:

很好的类比,但并不总是这样。有一些边缘情况不适合这个解释。假设我们有类PatientOrganPatient 可以有多个Organs,但一个Organ 只能属于一个Patient。当Patient被删除时,所有Organs也被删除。没有Patient,它们就无法存在。

以上是关于django OneToOneField 和 ForeignKey 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

Django ForeignKey 和 OneToOneField 之间的查询速度或区别是啥

django OneToOneField 和 ForeignKey 有啥区别?

Django OneToOneField、ManyToManyField、外键

ManyToManyField 的 Django OneToOneField 子集

Django -- 多表操作

为啥 django-rest-framework 不显示 OneToOneField 数据 - django