web-dev-qa-db-ja.com

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

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でこれをどのように表現できますか?

134
Naftuli Kay

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つのDudeまたは1つのBusinessに属し、DudeとBusinessesが複数のNumberを所有できるようになります。これはあなたが望んでいることだと思います。

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

Djangoでは、1対多の関係はForeignKeyと呼ばれます。ただし、一方向にしか機能しないため、クラスnumberDude属性を使用するのではなく、

class Dude(models.Model):
    ...

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

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

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

d.phonenumber_set.objects.all()を使用してPhoneNumberオブジェクトDudedsにアクセスし、Businessオブジェクトに対して同様にアクセスできます。

35
stillLearning

OneToMany関係の多くの側で外部キー(つまり、ManyToOne関係)を使用するか、一意の制約でManyToMany(任意の側で)を使用できます。

より明確にするために-DjangoにはOneToManyはなく、ManyToOneだけがあります-これは上記のForeignkeyです。 Foreignkeyを使用してOneToMany関係を記述できますが、それは非常に表現力に欠けています。

それに関する良い記事: https://amir.rachum.com/blog/2013/06/15/a-case-for-a-onetomany-relationship-in-Django/

13
validname

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を使用します。車cには、c.wheel_set.all()を使用します

2

「多くの」モデルがモデル自体の作成を正当化しない場合(ここではそうではありませんが、他の人に利益をもたらす可能性があります)、別の選択肢は Django Contribパッケージ

Postgresは Array または JSON のデータ型を処理できますが、これはmany -iesは、oneの単一のエンティティにのみ関連付けることができます。

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

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

アイデアを探している人の助けになることを願っています。

0
edouardtheron

ローリングストーン の答えは優れており、簡単で機能的ですが、解決されないことが2つあると思います。

  1. OPが電話番号を強制する場合、DudeとBusinessの両方に属することはできません
  2. デュード/ビジネスモデルではなく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)

詳細については docs を参照してください。簡単なチュートリアルについては この記事 をご覧ください。

また、こちらは 記事 Generic FKの使用をagainstと主張しています。

0
rabeye