16

この (おそらく単純な) 質問に対する答えを求めてスタック オーバーフローを検索しましたが、私が目にする解決策のほとんどは過度に複雑で理解しにくいようです。

抽象基本クラスであるモデル「Post」があります。モデル「アナウンスメント」と「イベント」は Post を継承しています。

現在、他のモデルのイベントとお知らせの関連リストを保持しています。たとえば、別のモデルに「removed_events」フィールドと「removed_announcements」フィールドがあります。

ただし、私のプロジェクトでは、「removed_events」と「removed_announcements」はまったく同じように扱われます。「削除されたイベント」と「削除されたお知らせ」を区別する必要はありません。つまり、「removed_posts」を追跡するフィールドで十分です。

Post は抽象的であるため、「removed_posts」フィールドを作成する方法がわかりません (または作成できない可能性があります)。しかし、今はコード内で自分自身を繰り返しているように感じます (そして、見ている投稿がイベントなのかお知らせなのかを判断し、適切な削除されたフィールド)。

ここで最良の選択肢は何ですか?Posts を非抽象化することはできますが、Post オブジェクト自体は作成しないでください。非抽象化オブジェクトにこれを強制することはできないと思います。

データベースについての私の理解は不十分ですが、Post を非抽象化すると結合のためにデータベースが複雑になるという印象を受けました。これは大したことですか?

最後に、他のモデルには、event_list と notifications_list に相当するものを post_list に要約したいフィールドが他にもありますが、これらのフィールドは明確にする必要があります。投稿タイプに基づいて post_list をフィルタリングできますが、filter() の呼び出しは、イベント リストとお知らせリストに個別に直接アクセスできる場合よりも遅くなりますね。ここに何か提案はありますか?

これを読んでくれてありがとう。

4

2 に答える 2

17

Django には 2 種類のモデルのサブクラス化があります - 抽象基底クラスです。マルチテーブル継承。

抽象基底クラスは単独で使用されることはなく、データベース テーブルや識別の形式もありません。これらは、データベースではなくコードで共通フィールドのセットをグループ化することにより、コードを短縮する方法にすぎません。

例えば:

class Address(models.Model):
    street = ...
    city = ...

    class Meta:
        abstract = True


class Employee(Address):
    name = ...

class Employer(Address):
    employees = ...
    company_name = ...

これは不自然な例ですが、ご覧のとおり、Employeeは ではなく、Addressどちらも ではありませんEmployer。どちらにも住所に関連するフィールドが含まれています。この例には 2 つのテーブルしかありません。Employee、およびEmployer- であり、両方とも Address のすべてのフィールドを含んでいます。雇用主の住所は、データベース レベルで従業員の住所と比較することはできません。住所には独自のキーがありません。

ここで、複数テーブルの継承 (Address から abstract=True を削除) を使用すると、Addressすべてそれ自体にテーブルを持ちます。これにより、3 つの異なるテーブルが作成されます。AddressEmployer、およびEmployee。雇用主と従業員の両方が、住所に戻る一意の外部キー (OneToOneField) を持ちます。

アドレスの種類を気にせずにアドレスを参照できるようになりました。

for address in Address.objects.all():
    try:
        print address.employer
    except Employer.DoesNotExist: # must have been an employee
        print address.employee

各アドレスには独自の主キーがあります。つまり、4 つ目のテーブルに独自に保存できます。

class FakeAddresses(models.Model):
    address = models.ForeignKey(Address)
    note = ...

マルチテーブル継承はPost、ポストのタイプを気にせずにタイプのオブジェクトを操作する必要がある場合に求められるものです。サブクラスから Post フィールドのいずれかにアクセスすると、結合のオーバーヘッドが発生します。ただし、オーバーヘッドは最小限に抑えられます。これは一意のインデックス結合であり、信じられないほど高速です。

へのアクセスが必要な場合は、クエリセットPostで使用することを確認してくださいselect_related

Events.objects.select_related(depth=1)

これにより、親データを取得するための追加のクエリが回避されますが、結合が発生します。そのため、投稿が必要な場合にのみ select related を使用してください。

最後に 2 つのメモ。投稿がお知らせとイベントの両方になる場合は、従来のことを行い、ForeignKey を介して投稿にリンクする必要があります。この場合、サブクラス化は機能しません。

最後に、結合が親と子の間でパフォーマンスが重要な場合は、抽象継承を使用する必要があります。Generic Relations を使用して、パフォーマンスの重要度がはるかに低いテーブルから抽象的な投稿を参照します。

一般的なリレーションは、基本的に次のようなデータを保存します。

class GenericRelation(models.Model):
    model = ...
    model_key = ...


DeletedPosts(models.Model):
    post = models.ForeignKey(GenericRelation)

これは SQL で結合するのがはるかに複雑になります (django がそれを支援します) が、単純な OneToOne 結合よりもパフォーマンスが低くなります。このルートをたどる必要があるのは、OneToOne 結合がアプリケーションのパフォーマンスに深刻な悪影響を与える可能性が低い場合だけです。

于 2011-04-13T22:57:36.010 に答える
2

ジェネリック リレーションシップと外部キーは、成功への道の友です。一方が一般的な中間モデルを定義し、もう一方がポリモーフィック モデルの関連リストを取得します。これは、標準の m2m 結合モデルよりも少し複雑です。ジェネリック側には 2 つの列があり、1 つは ContentType (実際には FK) に、もう 1 つは実際のリンクされたモデル インスタンスの PK に対応しています。標準の FK パラメータを使用して、リンクするモデルを制限することもできます。すぐに慣れます。

(これで、実際のキーボードを使用して書き込むことができたので、ここに例があります:)

class Post(models.Model):
    class Meta: abstract = True
    CONCRETE_CLASSES = ('announcement', 'event',)
    removed_from = generic.GenericRelation('OwnerRemovedPost',
        content_type_field='content_type',
        object_id_field='post_id',
    )

class Announcement(Post): pass

class Event(Post): pass

class Owner(models.Model):

    # non-polymorphic m2m
    added_events = models.ManyToManyField(Event, null=True)

    # polymorphic m2m-like property
    def removed_posts(self):
        # can't use ManyToManyField with through.
        # can't return a QuerySet b/c it would be a union.
        return [i.post for i in self.removed_post_items.all()]

    def removed_events(self):
        # using Post's GenericRelation
        return Event.objects.filter(removed_from__owner=self)


class OwnerRemovedPost(models.Model):
    content_type = models.ForeignKey(ContentType,
        limit_choices_to={'name__in': Post.CONCRETE_CLASSES},
    )
    post_id = models.PositiveIntegerField()
    post = generic.GenericForeignKey('content_type', 'post_id')
    owner = models.ForeignKey(Owner, related_name='removed_post_items')

    class Meta:
        unique_together = (('content_type', 'post_id'),)  # to fake FK constraint

従来の多対多のように関連するコレクションにフィルターをかけることはできませんが、 の適切なメソッドとOwner具象クラスのマネージャーを賢く使用することで、必要な場所に到達できます。

于 2011-04-13T21:47:40.843 に答える