django OneToOneField和ForeignKey有什么区别?

red*_*ice 353 django django-models

django OneToOneField和ForeignKey有什么区别?

Mat*_*kin 457

小心地意识到OneToOneField(SomeModel)和之间存在一些差异ForeignKey(SomeModel, unique=True).正如Django权威指南中所述:

OneToOneField

一对一的关系.从概念上讲,这类似于ForeignKeywith unique=True,但关系的"反向"方面将直接返回单个对象.

OneToOneField"反向"关系相反,ForeignKey"反向"关系返回a 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>
Run Code Online (Sandbox Code Playgroud)

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>]
Run Code Online (Sandbox Code Playgroud)

型号代码

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
Run Code Online (Sandbox Code Playgroud)

  • 这个答案说"存在一些差异",然后命名一个区别.还有其他人吗? (12认同)
  • @MarkPNeyer:据我所知,OneToOne字段只是:一对一.它不必是.见[**这个例子**](https://docs.djangoproject.com/en/dev/topics/db/examples/one_to_one/):一个地方不一定是餐馆. (5认同)
  • 我和克里斯一样想知道.它只是语法糖,是否存在一些潜在的数据访问差异,导致性能差异? (5认同)
  • 所以...什么时候甚至会想要使用`uniqueKey`和`unique = True`而不是`OneToOneField`?我在其他问题中看到,Django甚至警告说,OneToOneField`通常最符合一个人的利益.相反的`QuerySet`永远不会有多个元素,对吧? (4认同)
  • 根据您的描述,OneToOne 和ForeignKeyFields 在功能上似乎完全相同,因为它们可以在任何情况下用于完成完全相同的任务,但事实并非如此。两者之间在功能上的重要区别在于,来自不同对象的许多外键可能映射到单个对象,而使用 OneToOne 时,多个对象映射到单个对象是非法的。这个答案完全忽略了这一点,这确实是您需要了解的唯一重要的事情来选择使用哪个...如果您设置 unique=true 它们在功能上是相同的。 (4认同)
  • 是否有根本原因导致Django无法拥有这样的规则,即如果外键是唯一的而不是空值,那么`e.car`也可以工作吗? (3认同)
  • @Andy另一个区别是,使用ForeignKey的一个原因是在访问反向关系时避免错误。在上面的示例中,`engine_instance.car`可能引发_RelatedObjectDoesNotExist_异常,而`engine2_instance.car_set`最坏的情况是返回空查询集。为了使我的模型查询保持一致,我更喜欢使用`engine = models.ForeignKey(Engine2,unique = True,related_name ='car'`。那么,进行查询的唯一区别是在`xxx.filter(...)。all之间()用于多对一/多对多,而xxx.filter()。first()则用于一对一。 (2认同)

Dan*_*een 107

ForeignKey用于一对多,因此Car对象可能有许多Wheels,每个Wheel对其所属的Car具有ForeignKey.OneToOneField就像一个引擎,其中Car对象可以只有一个.

  • 是的:'OneToOneField与ForeignKey基本相同,但有一个例外,它始终带有一个"唯一"约束,反向关系总是返回指向的对象(因为只有一个),而不是返回一个清单". (7认同)
  • 这个答案中的术语有些混乱。根据官方 django 文档,ForeignKey 不是一对多,而是多对一的关系:https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db。模型.外键 (6认同)
  • 谢谢你,Dose OneToOneField(someModel)是指ForeignKey(SomeModel,unique = True)? (3认同)
  • @OlegTikhonov他们可能有相同引擎设计的_copy_,但我希望看到一个实例,其中有几辆车共享相同的物理引擎. (3认同)
  • 如果几辆汽车拥有相同的发动机怎么办? (2认同)

jet*_*d13 35

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

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

我们如何在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
Run Code Online (Sandbox Code Playgroud)

运行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()
Run Code Online (Sandbox Code Playgroud)

创建Article对象A1.

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

In [6]: A1.save()
Run Code Online (Sandbox Code Playgroud)

然后使用以下代码获取报告者的名称.

In [8]: A1.reporter.first_name
Out[8]: 'Rick'
Run Code Online (Sandbox Code Playgroud)

现在通过运行以下python代码创建Reporter对象R2.

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

In [10]: R2.save()
Run Code Online (Sandbox Code Playgroud)

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

In [13]: A1.reporter.add(R2)
Run Code Online (Sandbox Code Playgroud)

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

如您所见,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>]
Run Code Online (Sandbox Code Playgroud)

这个实际例子向我们展示了django ForeignKey用于定义多对一关系.

OneToOneField 用于创建一对一的关系.

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

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

我强烈建议尝试这个例子OneToOneField并实现差异.我很确定在这个例子之后你将完全知道django OneToOneField和django 之间的区别ForeignKey.


and*_*s52 12

OneToOneField(一对一)以面向对象的方式实现组合的概念,而ForeignKey(一对多)则与聚合有关.

  • 很好的类比,但并非总是那样。有一些极端的情况不适合这种解释。假设我们有类“患者”和“器官”。“患者”可以有许多“器官”,但是“器官”只能属于一个“患者”。删除“患者”后,所有“器官”也将被删除。没有“患者”就无法存在。 (2认同)

Dmi*_*sov 6

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

models.AutoField(primary_key=True)
Run Code Online (Sandbox Code Playgroud)

OneToOneField改为用作主键(UserProfile例如想象模型):

user = models.OneToOneField(
    User, null=False, primary_key=True, verbose_name='Member profile')
Run Code Online (Sandbox Code Playgroud)


小智 6

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

我有一个模型,像这样-

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
Run Code Online (Sandbox Code Playgroud)

现在的问题是我无法使用同一用户创建多个对象,即同一用户将在多天内出勤。因此,多个对象具有同一用户。

但 OneToOne 字段不允许我这样做。 参考图片

所以,我将模型更改为-

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
Run Code Online (Sandbox Code Playgroud)

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

这就是区别所在,OneToOne 字段不允许您使用同一用户创建多个对象(作为示例),但使用foreignkey 则可以。


Yup*_*up. 5

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

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

当您访问 ForeignKey 时,您将获得相关的模型对象,然后您可以对其进行进一步的查询。在这个例子中,同一本书模型的 'publisher' 字段是一个外键(与 Publisher 类模型定义相关):

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

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

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

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