Django の複数テーブルの継承 VS モデルでの明示的な OneToOne 関係の指定
-
22-09-2019 - |
質問
これがすべて意味があることを願っています:) 必要に応じて、コメントを通じて明確にします。また、この質問では太字のテキストを試していますが、私 (またはあなた) が気を散らす場合は編集して削除します。それはさておき...
django.contrib.auth を使用すると、ユーザーとグループをはじめ、これなしでは使えない便利なもの (基本的なメッセージングなど) が得られます。
私のアプリには、いくつかの異なるタイプのユーザーがいます。ユーザーは 1 つのタイプのみになれます。これは、少しだけ注意すれば、グループで簡単に対処できます。 ただし、これらの異なるユーザーは階層/関係で互いに関連しています。
これらのユーザーを見てみましょう。-
プリンシパル - 「トップレベル」ユーザー
管理者 - 各管理者はプリンシパルに直属します
コーディネーター - 各コーディネーターは管理者に直属します
これらとは別に 直接関係のない他のユーザー タイプもあります, しかし、後々関係するかもしれません。たとえば、「会社」もユーザーの一種であり、さまざまな「製品」を所有することができ、製品は「コーディネーター」によって監督される場合があります。「購入者」は、製品を購入する可能性のある別の種類のユーザーです。
さて、これらすべて ユーザーには他にもさまざまな属性があり、その中にはすべてのタイプのユーザーに共通するものもあれば、1 つのユーザー タイプにのみ異なる属性もあります。. 。たとえば、すべてのタイプのユーザーがアドレスを持っている必要があります。一方、「BranchOffice」にはプリンシパルユーザーのみが所属します。
もう一つのポイントは、上で述べたように、 ユーザーは 1 つのタイプのみになることができます.
アプリも プリンシパル、管理者、コーディネーター、会社、製品などを誰が作成および/または変更したかを追跡する必要がある. 。(つまり、User モデルへのリンクはあと 2 つあります。)
このシナリオでは、次のように Django のマルチテーブル継承を使用するのが得策でしょうか。-
from django.contrib.auth.models import User
class Principal(User):
#
#
#
branchoffice = models.ForeignKey(BranchOffice)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
#
#
#
それとも次のようにするべきですか:-
class Principal(models.Model):
#
#
#
user = models.OneToOneField(User, blank=True)
branchoffice = models.ForeignKey(BranchOffice)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
#
#
#
外部キーを介して関連付けられている他のユーザー タイプがあることに注意してください。次に例を示します。-
class Administrator(models.Model):
#
#
#
principal = models.ForeignKey(Principal, help_text="The supervising principal for this Administrator")
user = models.OneToOneField(User, blank=True)
province = models.ForeignKey( Province)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratorcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratormodifier")
Django がバックグラウンドで複数テーブルの継承に 1 対 1 の関係を使用していることは承知しています。どちらがより健全なアプローチであるかを判断するのに十分な資格が私にはありません。
解決
私は@thornomadによるソリューションを拡張したいと思います。
直接DjangoのUserクラスを拡張する内部django.authメカニズムとトラブルのすべての種類を引き起こす可能性があります。私は似たような状況でやったことは@thornomadが示唆正確に何である - 私は私自身のUserProfileのモデルは、私は追加のユーザデータを開催したDjangoのUserモデルとの一対一をリンク作って、私はさまざまなタイプのモデルを継承しているから、ユーザーのます。
あなたが説明したものを合わせて何かます:
class UserProfile(models.Model):
user = models.OneToOneField(User, blank=True, related_name='profile')
class Meta:
abstract = True
class PositionHolderUserProfile(UserProfile):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="created_users")
modified_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="modified_users")
class Principal(PositionHolderUserProfile):
branchoffice = models.ForeignKey(BranchOffice)
class Administrator(PositionHolderUserProfile):
superior = models.ForeignKey(Principal, related_name="subordinates")
province = models.ForeignKey(Province)
class Coordinator(PositionHolderUserProfile):
superior = models.ForeignKey(Administrator, related_name="subordinates")
class Company(UserProfile):
name = models.CharField(max_length=50)
class Product(models.Model):
name = models.CharField(max_length=50)
produced_by = models.ForeignKey(Company)
class Buyer(UserProfile):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
products_bought = models.ManyToManyField(Product)
他のヒント
私は最近、contrib.auto.models.User を継承するモデルの使用に切り替えました。私の一般的な見解は、理論的には優れていますが、場合によっては期待どおりに自動的に魔法のように処理されないことです。
相続に関するあなたの決断と、OneToOne は次のようになります。
- Django に 95% の確率で自動的に何かを正しく実行させ、残りの 5% をデバッグする必要があるようにしたいですか?
-または-
- 100% の場合、自分で手動で何かを行いたいですか?
まだ見ていない場合は、Scott Barham ブログに、ユーザーからの継承と、カスタム オブジェクトが確実に返されるようにするためのカスタム バックエンドの構築に関する素晴らしい投稿があります。 Django ユーザーの拡張.
さらに興味深いのは、によって提供される AutoOneToOne フィールドです。 ジャンゴ迷惑. 。これは、2 つのアプローチのハイブリッドのようなものです。継承は行われませんが、一致する OneToOneField が存在しない場合は、Django がそれを作成します。
また、 ソーノマッド モデルの冗長性については適切な指摘をしています。これをクリーンアップする抽象クラスを簡単に実装できます (手動で OneToOne を実行していると仮定して)。
class BaseExtendedUser(models.Model):
user = models.OneToOneField(User, blank=True, related_name='profile')
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="created_users")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="modified_users")
class Meta:
abstract = True
class Administrator(BaseExtendedUser):
province = models.ForeignKey(Province)
class Principal(BaseExtendedUser):
branchoffice = models.ForeignKey(BranchOffice)
私はむしろカスタムUser
を使用し、私はUserProfile
モデルを継承するとは思わない - だけでcontrib.auth
モデルを残します。カスタムUserProfile
モデルでは、あなたの可能性セットアップA のベースのすべての異なるユーザータイプの一部にすることができ、ユーザプロファイルモデルます。
だけはすぐにそれを見て、あまりにも、私は(あなたの最後の2つのPrinciple
とAdministrator
モデルなど)すべて同じフィールドを繰り返して任意のモデルに慎重になります。ユーザープロファイルのアイデアをグループ機能に建てを組み合わせることで、あなたが探しているものを行うことができます。
コーディネーターが校長に昇進しますと、データモデルで何が起こるかを検討してください。私はすべてで、この場合の継承を使用することはありません。 「あなたが探しているものを行うことができ、ユーザプロファイルのアイデアをグループ機能に建て組み合わせる。」以前のポスターの提案を再考してください。
あなたはauth.Userのどこかのように動作し、ユーザークラスのオブジェクトが必要ですか?それはOneToOne以上の継承を使用するための最も明白な理由だろう。一つのOneToOneアプローチのプロそれが心配だ場合は、別のユーザーモデルに切り替えることが可能な容易になります。
私はあなたが(いずれかの方法で)上記の持っているものと見本当の問題は、同じユーザープリンシパルオブジェクトと管理オブジェクトのシェアを持っていることからあなたを停止するものがあるように表示されないことです。 OneToOneFieldのみ任意の2人の関係の間に1対1のマッピングを保証することができます。