Django OneToOneFieldとForeignKeyの違いは何ですか?
OneToOneField(SomeModel)
とForeignKey(SomeModel, unique=True)
にはいくつかの違いがあることに注意してください。 The Dfingo Guide to Django :
OneToOneField
1対1の関係。概念的には、これは
unique=True
を持つForeignKey
に似ていますが、リレーションの「逆」側は単一のオブジェクトを直接返します。
OneToOneField
「逆」リレーションとは対照的に、ForeignKey
「逆」リレーションはQuerySet
を返します。
たとえば、次の2つのモデルがある場合(以下の完全なモデルコード):
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>
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
ForeignKeyは1対多であるため、Carオブジェクトには多数のホイールがあり、各ホイールには所属するCarに対するForeignKeyがあります。 OneToOneFieldはエンジンのようなもので、Carオブジェクトには1つしかありません。
新しいことを学ぶための最良かつ最も効果的な方法は、実世界の実用的な例を見て研究することです。しばらくの間、レポーターがニュース記事を書いて公開できる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
の違いが完全にわかると確信しています。
OneToOneField(1対1)はオブジェクト指向で構成の概念を実現し、ForeignKey(1対多)は集約に関連します。
また、OneToOneField
は、キーの重複を避けるために主キーとして使用すると便利です。暗黙的/明示的な自動フィールドがない場合があります
models.AutoField(primary_key=True)
ただし、代わりにOneToOneField
を主キーとして使用します(たとえば、UserProfile
モデルを想像してください):
user = models.OneToOneField(
User, null=False, primary_key=True, verbose_name='Member profile')
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に追加することにより生成されます。
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値に対応する複数のレコードが含まれる場合があります。