django OneToOneFieldとForeignKeyの違いは何ですか?


回答:


507

いくつかの違いがあることを実感するように注意してくださいOneToOneField(SomeModel)とはForeignKey(SomeModel, unique=True)Djangoの決定的なガイドで述べたように:

OneToOneField

1対1の関係。概念的には、これはForeignKeywith unique=Trueに似ていますが、関係の「逆」側は直接単一のオブジェクトを返します。

OneToOneField「逆」関係とは対照的に、「ForeignKey逆」関係はを返しますQuerySet

たとえば、次の2つのモデルがある場合(以下の完全なモデルコード):

  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>

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

5
@MarkPNeyer:私が理解している限り、OneToOneフィールドは1対1です。上にある必要はありません。この例を参照してください。場所はレストランである必要はありません。
osa

21
この答えは「いくつかの違いがあります」と言ってから、1つの違いを挙げます。他にありますか?
Chris Martin、

6
クリスと同じかしら。それは単に構文上の砂糖ですか、データへのアクセス方法に根本的な違いがあり、パフォーマンスの違いにつながりますか?
Carlos

4
Djangoが外部キーが一意でnullでない場合にe.carも機能するようなルールを設定できない根本的な理由はありますか?
Ciro Santilli郝海东冠状病六四事件法轮功

4
だから... ではなくForeignKeywith を使用したいのunique=TrueOneToOneFieldいつですか?他の質問では、DjangoがOneToOneField通常は最善のサービスを自分の利益に提供することを警告していることもあります。逆QuerySetは、複数の要素を持つことはありませんよね?
アンディ

121

ForeignKeyは1対多のものであるため、Carオブジェクトには多数のホイールがあり、各ホイールには所属するCarへのForeignKeyがあります。OneToOneFieldはエンジンのようなもので、Carオブジェクトは1つだけ持つことができます。


4
ありがとう、Dose OneToOneField(someModel)はForeignKey(SomeModel、unique = True)を意味しますか?
11年

9
はい: 'OneToOneFieldは基本的にForeignKeyと同じですが、常に「一意の」制約を持ち、逆のリレーションは常にオブジェクトを返すので(常に1つしかないため)、リスト。'
Dan Breen、

1
同じエンジンを持ついくつかの車はどうですか?
Oleg Belousov

3
@OlegTikhonov彼らは同じエンジン設計のコピーを持っているかもしれませんが、複数の車が同じ物理エンジンを共有している例が欲しいです。
Dan Breen

3
この回答の用語については少し混乱があります。ForeignKeyのは、1対多のではないですが、それは公式のDjangoのドキュメントによると、多対1の関係である:docs.djangoproject.com/en/2.0/ref/models/fields/...
Kutay Demireren

45

新しいことを学ぶための最良かつ最も効果的な方法は、実際の実例を見て​​研究することです。とりあえず、記者がニュース記事を書いて公開できるdjangoでブログを構築したいとします。オンライン新聞の所有者は、各記者が必要なだけ多くの記事を発行できるようにしたいと考えていますが、同じ記事に対して別の記者が作業することは望んでいません。これは、読者が記事を読み読みするときに、記事の著者は1人だけであることを意味します。

例:Johnによる記事、Harryによる記事、Rickによる記事。上司は2人以上の著者に同じ記事での作業を望まないため、Harry&Rickによる記事を作成することはできません。

ジャンゴの助けを借りて、この「問題」をどのように解決できますか?この問題を解決する鍵は、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コードを実行し、データベースにアプリのテーブルを作成します。次に、を使用python manage.py shellしてPythonシェルを開きます。

ReporterオブジェクトR1を作成します。

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

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

In [51]: R1.save()

Articleオブジェクト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)

これは機能せず、「Reporter」オブジェクトに属性「add」がないことを示す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が多対1の関係を定義するために使用されていることを示しています。

OneToOneField 1対1の関係を作成するために使用されます。

reporter = models.OneToOneField(Reporter)上記のmodels.pyファイルで使用できますが、作成者は複数の記事を投稿できないため、この例では役に立ちません。

新しい記事を投稿するたびに、新しいReporterオブジェクトを作成する必要があります。これは時間がかかりますね。

で例を試してOneToOneField、違いを理解することを強くお勧めします。この例の後で、django OneToOneFieldとdjangoの違いが完全にわかると思いますForeignKey


私はこれが好き。OneToOneとForeignKeyの根本的な違いは、1対1と1対多の関係です。ForeignKeyとunique = Trueを使用して1対1で行うことができますが、微妙な違いはマシューの返信に記載されています。
FrankZhu

13

OneToOneField(1対1)はオブジェクト指向で合成の概念を実現しますが、ForeignKey(1対多)は集約に関連します。


3
いい例えですが、いつもそうとは限りません。この説明に当てはまらないエッジケースがいくつかあります。たとえば、クラスPatientとがあるとしOrganます。Patientは多くOrganのを持つことができますが、Organ1つだけに属することができPatientます。ときにPatient削除され、すべてOrganのは、あまりにも削除されます。なしでは存在できませんPatient
セザール

4

またOneToOneField、キーの重複を回避するために主キーとして使用すると便利です。暗黙的/明示的な自動フィールドがない場合があります

models.AutoField(primary_key=True)

OneToOneField代わりに主キーとして使用してください(UserProfileたとえば、モデルを想像してください):

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

3

OneToOneFieldにアクセスすると、照会したフィールドの値を取得します。この例では、書籍モデルの「title」フィールドはOneToOneFieldです。

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

ForeignKeyにアクセスすると、関連するモデルオブジェクトが取得されます。このオブジェクトを使用して、さらにクエリを実行できます。この例では、同じ本モデルの 'publisher'フィールドは、(Publisherクラスモデル定義に対応する)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に追加することによって生成されます。


1

OneToOneField:2番目のテーブルが関連付けられている場合

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

table2には、table1のpk値に対応するレコードが1つだけ含まれます。つまり、table2_col1には、テーブルのpkに等しい一意の値が含まれます。

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

table2には、table1のpk値に対応する複数のレコードが含まれる場合があります。


1

ForeignKeyを使用すると、別のクラスの定義であるサブクラスを受け取ることができますが、OneToOneFieldsはこれを行うことができず、複数の変数にアタッチできません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.