[解決済み] Django の OneToOne, ManyToMany, ForeignKey フィールドの違いは何ですか?
質問
Django のモデルでリレーションシップについて理解するのに少し苦労しています。
誰か OneToOne, ManyToMany, ForeignKey の違いを説明してくれませんか?
どのように解決するのですか?
さて、ここで本質的に2つの疑問があります。
- 1 対 1、多対多、および外部キー リレーションの違い (一般的に) は何ですか。
- Django に特有のそれらの違いは何ですか。
これらの質問の両方は、単純なGoogle検索によって非常に簡単に答えることができますが、私はSOでこの質問の正確な重複を見つけることができないので、私は先に行くと、答えるでしょう。
Djangoでは、リレーションシップは片方だけで定義することに注意してください。
外部キー
外部キーリレーションは、一般に多対一のリレーションとして知られています。この関係の逆は一対多であることに注意して下さい (Django はこれにアクセスするためのツールを提供しています)。その名が示すように、多くのオブジェクトが 1 つのオブジェクトに関連することがあります。
Person >--| Birthplace
^ ^
| |
Many One
この例では、人は1つの出生地しか持たないかもしれませんが、出生地は多くの人に関係する可能性があります。この例を Django で見てみましょう。これらが私たちのモデルだとします。
class Birthplace(models.Model):
city = models.CharField(max_length=75)
state = models.CharField(max_length=25)
def __unicode__(self):
return "".join(self.city, ", ", self.state)
class Person(models.Model):
name = models.CharField(max_length=50)
birthplace = models.ForeignKey(Birthplace)
def __unicode__(self):
return self.name
の中では何の関係も定義されていないことがわかります。
Birthplace
モデル内ではリレーションは定義されておらず
ForeignKey
の関係が定義されています。
Person
モデルの中で定義されます。次のようなモデルのインスタンスを作成するとします(明らかにPythonの構文ではありません)。
- 生まれ故郷。テキサス州ダラス
- 出生地 ニューヨーク州ニューヨーク市
- 人物 ジョン・スミス(John Smith) 出身地:(テキサス州ダラス
- 人 マリア・リー、出生地:(テキサス州ダラス)
- 人 ダニエル・リー(Daniel Lee)、出生地:(ニューヨーク州ニューヨーク市
さて、Django がこれらのリレーションをどのように利用できるかを見てみましょう (注:このリレーションは
./manage.py shell
はあなたの友達です!)。
>> from somewhere.models import Birthplace, Person
>> Person.objects.all()
[<Person: John Smith>, <Person: Maria Lee>, <Person: Daniel Lee>]
>> Birthplace.objects.all()
[<Birthplace: Dallas, Texas>, <Birthplace: New York City, New York>]
作成したモデルインスタンスを見ることができます。では、ある人の出身地を調べてみましょう。
>> person = Person.object.get(name="John Smith")
>> person.birthplace
<Birthplace: Dallas, Texas>
>> person.birthplace.city
Dallas
ある出生地を持つ全ての人を見たいとしましょう。先に述べたように、 Django では逆関係にアクセスすることができます。デフォルトでは、Django はマネージャ (
RelatedManager
) をモデル上に作成し、その名前は
<model>_set
という名前のモデルで、ここで
<model>
は小文字のモデル名です。
>> place = Birthplace.objects.get(city="Dallas")
>> place.person_set.all()
[<Person: John Smith>, <Person: Maria Lee>]
このマネージャの名前は
related_name
キーワード引数を設定することで変更できます。ですから、私たちは
birthplace
フィールドを
Person
モデルに追加します。
birthplace = models.ForeignKey(Birthplace, related_name="people")
これで、きれいな名前でその逆リレーションシップにアクセスできるようになりました。
>> place.people.all()
[<Person: John Smith>, <Person: Maria Lee>]
一対一
1対1の関係は、2つのオブジェクトが一意の関係を持つように制限されることを除けば、多対1の関係に非常に似ています。この例として、ユーザーとプロファイル(ユーザーに関する情報を保存するもの)があります。2人のユーザーが同じプロファイルを共有することはありません。
User |--| Profile
^ ^
| |
One One
これを Django で見てみましょう。ユーザーモデルは Django が定義してくれるので、わざわざ定義する必要はないでしょう。しかし、 Django は、ユーザモデルとして
django.contrib.auth.get_user_model()
を使ってユーザをインポートすることを推奨しているので、そうすることにします。プロファイルモデルは以下のように定義できます。
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL) # Note that Django suggests getting the User from the settings for relationship definitions
fruit = models.CharField(max_length=50, help_text="Favorite Fruit")
facebook = models.CharField(max_length=100, help_text="Facebook Username")
def __unicode__(self):
return "".join(self.fruit, " ", self.facebook)
必要なのはシェルでこれをテストするためのプロファイルを持った1人のユーザだけです。
- ユーザー: johndt6
- プロフィール:ユーザー:johndt6, "Kiwi", "blah_blah"
これで、Userモデルからユーザーのプロファイルに簡単にアクセスできるようになりました。
>> user = User.objects.all()[0]
>> user.username
johndt6
>> user.profile
<Profile: Kiwi blah_blah>
>> user.profile.fruit
Kiwi
>> profile = Profile.objects.get(user=user)
>> profile.user
<User: johndt6>
もちろん、逆リレーションの名前は
related_name
引数を使ってカスタマイズすることもできます。
多対多
多対多のリレーションは少し厄介な場合があります。まず、多対多のフィールドは厄介であり、可能な限り避けるべきであると言うことから始めましょう。しかし、多対多のリレーションシップが意味を持つ場面はたくさんあります。
2つのモデル間の多対多の関係は、最初のモデルの0個、1個またはそれ以上のオブジェクトが、2番目のモデルの0個、1個またはそれ以上のオブジェクトに関連している可能性があることを定義します。例として、プロジェクトを通じてワークフローを定義している会社を想定してみましょう。プロジェクトは、注文なし、1つの注文のみ、または多くの注文に関連する可能性がある。注文は、プロジェクトなし、1つのプロジェクト、または多くのプロジェクトに関連する可能性があります。
Order >--< Project
^ ^
| |
Many Many
このようにモデルを定義してみましょう。
class Order(models.Model):
product = models.CharField(max_length=150) # Note that in reality, this would probably be better served by a Product model
customer = models.CharField(max_length=150) # The same may be said for customers
def __unicode__(self):
return "".join(self.product, " for ", self.customer)
class Project(models.Model):
orders = models.ManyToManyField(Order)
def __unicode__(self):
return "".join("Project ", str(self.id))
Django は
RelatedManager
に対して
orders
フィールドを使用して多対多の関係にアクセスすることができます。
次のようなモデルのインスタンスを作成しましょう(私の一貫性のない構文で!)。
- 順序: "Spaceship", "NASA"
- オーダー: "潜水艦"、"米海軍"
- オーダー: "レースカー"、"NASCAR"
- プロジェクト: 注文。[]
- プロジェクト: 注文。[(オーダー: "宇宙船", "NASA")]
- プロジェクト: 注文。(オーダー: "宇宙船", "NASA")、(オーダー: "レースカー", "NASCAR") ]である。
これらの関係には、以下のようにアクセスできます。
>> Project.objects.all()
[<Project: Project 0>, <Project: Project 1>, <Project: Project 2>]
>> for proj in Project.objects.all():
.. print(proj)
.. proj.orders.all() # Note that we must access the `orders`
.. # field through its manager
.. print("")
Project 0
[]
Project 1
[<Order: Spaceship for NASA>]
Project 2
[<Order: Spaceship for NASA>, <Order: Race car for NASCAR>]
NASAの注文は2つのプロジェクトに関連し、米海軍の注文は何も関連していないことに注意してください。また、1 つのプロジェクトには注文がなく、1 つのプロジェクトには複数あることにも注意してください。
また、前と同じように関係を逆からアクセスすることもできます。
>> order = Order.objects.filter(customer="NASA")[0]
>> order.project_set.all()
[<Project: Project 0>, <Project: Project 2>]
ASCIIカーディナリティガイド
ASCIIの図が少しわかりにくいかもしれませんが、以下の説明が参考になるかもしれません。
-
>
または<
は、「多くの人に」という意味です。 -
|
は「一人に」という意味です。
だから...
A --| B
は、AのインスタンスがBのインスタンスに1つだけ関連づけられることを意味します。
また
A --< B
は、AのインスタンスがBの多くのインスタンスに関連することができることを意味します。
A >--< B
は...と等価です。
A --< B
A >-- B
このように、関係の "side" または方向は、それぞれ別々に読むことができます。ただ、一緒につぶすと便利なのです。
これらの関係の一つを拡張することは、より意味があるかもしれません。
+---- John Smith
|
Dallas|-------+---- Jane Doe
|
+---- Joe Smoe
リソース
dbの関係性の良い説明 提供:@MarcB
Django のドキュメントです。
関連
-
[解決済み】Pythonのvirtualenvを離脱/終了/無効化する方法
-
[解決済み] staticmethodとclassmethodの違いについて
-
[解決済み] Pythonのリストメソッドであるappendとextendの違いは何ですか?
-
[解決済み] オブジェクト名の前のシングルアンダーコアとダブルアンダーコアの意味は何ですか?
-
[解決済み] re.searchとre.matchの違いは何ですか?
-
[解決済み] eval、exec、compileの違いは何ですか?
-
[解決済み】__str__と__repr__の違いは何ですか?
-
[解決済み】Djangoでnull=Trueとblank=Trueの違いは何ですか?
-
[解決済み] SQLAlchemy: セッションの作成と再利用
-
[解決済み] PythonのRequestsモジュールを使ってWebサイトに "ログイン "するには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Jupyterノートブックでenv変数を設定する方法
-
[解決済み] SQLAlchemy: セッションの作成と再利用
-
[解決済み] PythonでファイルのMD5チェックサムを計算するには?重複
-
[解決済み] Django のテストデータベースをメモリ上だけで動作させるには?
-
[解決済み] Python 3でバイナリデータを標準出力に書き込むには?
-
[解決済み] 値で列挙名を取得する [重複]。
-
[解決済み] Jupyter (IPython)ノートブックのセッションをpickleして保存する方法
-
[解決済み] Pandasのデータフレーム内の文字列を'date'データ型に変換するにはどうしたらいいですか?
-
[解決済み] pycharmがタブをスペースに自動変換する
-
[解決済み] データクラスとtyping.NamedTupleの主な使用例