Djangoで1対多の関係を表現する方法


167

私は今Djangoモデルを定義していますOneToManyFieldが、モデルフィールドタイプにはがないことに気付きました。これを行う方法があると確信しているため、何が欠けているのかわかりません。私は基本的に次のようなものを持っています:

class Dude(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

class PhoneNumber(models.Model):
    number = models.CharField()

この場合、それぞれがDude複数持つことができるPhoneNumberのが、私から知っている必要がないという点で関係は、単方向でなければなりませんPhoneNumberどのDude私は、多くの異なるオブジェクトを持っている可能性があるとして、それ自体は、それを所有していることを自分PhoneNumberのようなインスタンスBusinessのために例:

class Business(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

OneToManyFieldこの種類の関係を表すには、モデル内で何を置き換えますか(存在しません)。私はHibernate / JPAから来ており、1対多の関係の宣言は次のように簡単でした。

@OneToMany
private List<PhoneNumber> phoneNumbers;

これをDjangoでどのように表現できますか?

回答:


133

Djangoで1対多の関係を処理するには、を使用する必要がありますForeignKey

ForeignKeyのドキュメントは非常に包括的であり、あなたが持っているすべての質問に答えるべきです:

https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey

例の現在の構造では、各Dudeが1つの番号を持ち、各番号が複数のDudesに属することができます(Businessと同じ)。

逆の関係が必要な場合は、2つのForeignKeyフィールドをPhoneNumberモデルに追加する必要があります。1つはDude用で、もう1つはBusiness用です。これにより、各番号を1つのデュードまたは1つのビジネスに所属させることができ、デュードとビジネスが複数の番号を所有できるようになります。これはあなたが求めているものかもしれないと思います。

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)

3
上記の問題の例を挙げていただけますか?私はおそらくそれを完全に見逃しているだけですが、私はDjangoのドキュメントをしばらく読んでいますが、この種の関係を作成する方法についてはまだはっきりしていません。
Naftuli Kay、2011

4
両方のForeignKeysを必要としないようにするか(空白= True、null = True)、または何らかのカスタム検証を追加して、少なくとも一方が存在することを確認できます。一般的な番号を持つ企業の場合はどうですか?または失業者?
j_syk 2011

1
@j_sykカスタム検証の良い点。しかし、外部キーとビジネスの両方の外部キーを含めてから、カスタム(モデル定義の外部)検証を行うのは、ちょっとハッカらしいようです。よりクリーンな方法が必要なようですが、私もそれを理解することができません。
アンディ2013年

この状況では、異なるオブジェクトへの一般的な関係を可能にするContentTypesフレームワークを使用することが適切です。ただし、コンテンツタイプを使用すると、非常に複雑になる可能性が非常に高く、これが唯一のニーズである場合は、この単純な(ハックの場合)アプローチが望ましい場合があります。
kball 2013年

57
言及すべき1つの関連事項は、ForeignKeyの "related_name"引数です。したがって、PhoneNumberクラスでは、関連するすべての番号を取得するためにdude = models.ForeignKey(Dude, related_name='numbers')使用できますsome_dude_object.numbers.all()( "related_name"を指定しない場合、デフォルトで "number_set"になります)。
マークシェップ2014

40

Djangoでは、1対多の関係はForeignKeyと呼ばれます。ただし、これは一方向にしか機能しないため、必要なnumberクラスの属性でDudeはなく

class Dude(models.Model):
    ...

class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)

多くのモデルはForeignKey他の1つのモデルに対応できるため、次のPhoneNumberような2番目の属性を持つことは有効です。

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)

あなたはアクセスできるPhoneNumberため、SをDude対象dd.phonenumber_set.objects.all()し、その後についても同様に行うBusinessオブジェクト。


1
私はForeignKey「1対1」を意味する仮定の下にありました。上記の例を使用して、私Dudeは多くのPhoneNumbers権利を持っている必要がありますか?
Naftuli Kay、2011

6
私はこれを反映するように私の回答を編集しました。はい。ForeignKeyを指定した場合ForeignKey(Dude, unique=True)、1対1のみになるため、上記のコードではDude、複数PhoneNumberのを使用してを取得します。
stillLearning

1
@rolling stone-ありがとう、私はあなたがコメントしたように私の間違いを理解した後でそれを追加していました。Unique = TrueはOneToOneFieldとまったく同じようには機能しません。Unique= Trueを指定した場合、ForeignKeyは1対1の関係のみを使用することを説明しました。
stillLearning

8
+1をから実行しPhoneNumberます。今、それは理にかなっています。ForeignKey基本的に
多対1なので

2
誰かがフィールドの名前の重要性を説明できますかphonenumber_set?どこにも定義されていません。それはモデル名で、すべて小文字で "_set"が付いていますか?
James Wierzba


15

OneToManyリレーションの多くの側(つまり、ManyToOneリレーション)で外部キーを使用するかManyToMany、一意制約を使用して(任意の側で)使用できます。


8

django十分スマートです。実際、oneToManyフィールドを定義する必要はありません。自動的に生成さdjangoれます:-)。foreignKey関連するテーブルで定義するだけです。つまり、を使用してManyToOne関係を定義するだけで済みますforeignKey

class Car(models.Model):
    // wheels = models.oneToMany() to get wheels of this car [**it is not required to define**].


class Wheel(models.Model):
    car = models.ForeignKey(Car, on_delete=models.CASCADE)  

特定の車のホイールのリストを取得したい場合。python's自動生成されたオブジェクトを使用しますwheel_set。お車cc.wheel_set.all()


5

一方で転がる石の答えは良いですが、簡単で機能的な、私はそれが解決しない二つのものがあると思います。

  1. OPが電話番号を強制したい場合、DudeとBusinessの両方に属することはできません。
  2. Dude / Businessモデルではなく、PhoneNumberモデルで関係を定義した結果として生じる避けられない悲しみ。地球に余分な地球がやって来て、エイリアンモデルを追加したい場合は、単に "phone_numbers"フィールドをエイリアンモデルに追加する代わりに、PhoneNumber(ETに電話番号があると仮定)を変更する必要があります。

コンテンツタイプフレームワークを導入します。これにより、PhoneNumberモデルに「汎用外部キー」を作成できるオブジェクトがいくつか公開されます。次に、DudeとBusinessの逆の関係を定義できます。

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models

class PhoneNumber(models.Model):
    number = models.CharField()

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    owner = GenericForeignKey()

class Dude(models.Model):
    numbers = GenericRelation(PhoneNumber)

class Business(models.Model):
    numbers = GenericRelation(PhoneNumber)

詳細についてはドキュメントを参照してください。簡単なチュートリアルについては、おそらくこの記事をチェックしてください。

また、ジェネリックFKの使用に反対する記事もあります。


0

「多くの」モデルがモデル自体の作成を正当化しない場合(ここでは当てはまりませんが、他の人にメリットがある可能性があります)、別の代替案は、Django Contribパッケージを介して特定のPostgreSQLデータ型に依存することです。

Postgresは配列またはJSONデータ型を処理できます。これは、多対多1つの単一エンティティにのみ関連付けることができる場合に、1対多を処理するための優れた回避策です。

Postgresを使用すると、配列の単一の要素にアクセスできます。つまり、クエリが非常に高速になり、アプリケーションレベルのオーバーヘッドを回避できます。そしてもちろん、Djangoはこの機能を活用するための素晴らしいAPI実装しています

それは明らかに他のデータベースのバックエンドに移植できないという欠点がありますが、私はまだ言及する価値があると思います。

それがアイデアを探している一部の人々に役立つことを願っています。


0

まず、ツアーを始めます。

01)1対多の関係:

ASSUME:

class Business(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class Dude(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class PhoneNumber(models.Model):
    number = models.CharField(max_length=20)
    ........
    ........

注意:DjangoはOneToMany関係を提供していません。そのため、Djangoでは上部メソッドを使用できません。しかし、リレーショナルモデルで変換する必要があります。それでは、何ができるでしょうか?この状況では、リレーショナルモデルをリバースリレーショナルモデルに変換する必要があります。

ここに:

リレーショナルモデル= OneToMany

したがって、逆関係モデル= ManyToOne

注意:DjangoはManyToOne関係をサポートしています。Djangoでは、ManyToOneはForeignKeyによって表されます。

02)多対1の関係:

SOLVE:

class Business(models.Model):
    .........
    .........

class Dude(models.Model):
    .........
    .........

class PhoneNumber(models.Model):
    ........
    ........
    business = models.ForeignKey(Business)
    dude = models.ForeignKey(Dude)

注意:簡単に考えてください!!

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