web-dev-qa-db-ja.com

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

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

332
redice

OneToOneField(SomeModel)ForeignKey(SomeModel, unique=True)にはいくつかの違いがあることに注意してください。 The Dfingo Guide to Django

OneToOneField

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

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>

unique=Trueを使用したForeignKeyの例

>>> 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
431
Matthew Rankin

ForeignKeyは1対多であるため、Carオブジェクトには多数のホイールがあり、各ホイールには所属するCarに対するForeignKeyがあります。 OneToOneFieldはエンジンのようなもので、Carオブジェクトには1つしかありません。

97
Dan Breen

新しいことを学ぶための最良かつ最も効果的な方法は、実世界の実用的な例を見て研究することです。しばらくの間、レポーターがニュース記事を書いて公開できるDjangoにブログを構築したいとします。オンライン新聞の所有者は、各記者が必要な数の記事を公開できるようにしたいと考えていますが、同じ記者に別の記者が作業することを望んでいません。これは、読者が記事を読みに行くときに、記事の著者を1人だけ選択することを意味します。

例:ジョンの記事、ハリーの記事、リックの記事。上司は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の関係を作成するために使用されます。

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

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

OneToOneFieldを使用して例を試して、違いを理解することを強くお勧めします。この例の後、Django OneToOneFieldとDjango ForeignKeyの違いが完全にわかると確信しています。

31
jetbird13

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

11
andrers52

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

models.AutoField(primary_key=True)

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

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

OneToOneFieldにアクセスすると、クエリしたフィールドの値を取得します。この例では、ブックモデルの「タイトル」フィールドはOneToOneFieldです。

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

ForeignKeyにアクセスすると、関連するモデルオブジェクトを取得し、それに対してさらにクエリを実行できます。この例では、同じ書籍モデルの「パブリッシャー」フィールドは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に追加することにより生成されます。

2
Yup.

OneToOneField:2番目のテーブルが関連している場合

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

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

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

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

0
aarif faridi