におけるモデル継承


問題


私は現在、医療機器を販売する会社のために働いています.数ヶ月前、私たちの販売担当者が簡単に私たちの製品を検索し、お客様に送信するための引用符を構築することができますdjangoを使用してシステムを構築することを求められた.システムの要件のいくつかは以下の通りです.
  • 我々はいくつかの製品の種類があります.すべての型に共通の属性と、特定の型に固有の属性があります.
  • 我々はすべての製品の種類を一度に検索できるようにする必要があります.
  • REPは複数の引用を持つことができます、そして、各々の引用は一つ以上の製品を持ちます.その製品はどんなタイプであってもよい.
  • それぞれの製品タイプは、djangoの管理サイトに独自のセクションを持つ必要がありますので、対応するプロダクトマネージャーは、データを管理して管理することができます.
  • この時点で、継承が私たちのデータモデルで大きな役割を果たすことは、私にとってかなり明確でした.継承は、各要件をうまく解決します.
  • 基地があったProduct すべての型に共通の属性を持つクラスは、各製品の型は、独自の、一意の属性を追加するサブクラスにすることができます.
  • すべての製品の種類をまたがって検索すると、何かを検索するように簡単ですProduct
  • 引用符のリストを保存することができますProduct オブジェクトタイプと無知のまま
  • それぞれのサブクラスを登録できますProduct Djangoの管理サイトに個別に
  • 私にまだはっきりしていなかったことは、継承がデータベースにどう翻訳されるべきかであった.Djangoはリレーショナルデータベースでのみ動作し、以前はリレーショナルデータベースで継承をモデリングしようとしたことがありませんでした.いくつかの研究を行った後、私はリレーショナルデータベースの継承のモデリングの2つの主要な方法に遭遇した.私は、私たちが選んだそれぞれの方法と理由を議論したい.

    複数テーブル継承


    複数テーブル継承は、スーパークラスと各サブクラスが自分のデータベーステーブルにマップする場所です.共有フィールドは、SuperClassテーブルに格納され、型に固有のフィールドがそれぞれのテーブルに格納されます.各サブクラステーブルには、親クラステーブルの対応する行のIDが含まれている列が追加されます.Djangoでは、モデルクラスはこのように見えるかもしれません
    from django.db import models
    
    class Product(models.Model):
      part_number = models.CharField(max_length=25)
      price = models.CharField(max_length=25)
    
    class Label(Product):
      dimensions = models.CharField(max_length=25)
    
    class FaceMask(Product):
      COLOR_CHOICES = [
          ('Blue', 'Blue'),
          ('White', 'White')
      ]
      color = models.CharField(max_length=25, choices=COLOR_CHOICES)
    
    そして、テーブルはこのようになります

    グレート.今、我々は個々に編集することができます管理者に各製品の種類を追加することができます.また、一般的な製品を追加することができますQuote このように
    class Quote(models.Model):
      name = models.CharField(max_length=25)
      products = models.ManyToManyField(Product)
    

    単一テーブル継承


    名前が意味するように、単一のテーブル継承は、スーパークラスの属性とすべてのサブクラスを格納するために1つのテーブルだけを使用します.サブクラス型をテーブルで区別する代わりにtype 列は、特定の行が対応するオブジェクトの型を知るために使用されます.Djangoでは以下のようになります.
    class Product(models.Model):
      # Type field
      TYPE_CHOICES = [
        ('Label', 'Label'),
        ('Face Mask', 'Face Mask')
      ]
      product_type = models.CharField(max_length=25, choices=TYPE_CHOICES)
    
      # Common Fields
      part_number = models.CharField(max_length=25)
      price = models.CharField(max_length=25)
    
      # Label fields
      label_dimensions = models.CharField(max_length=25)
    
      # Face Mask fields
      COLOR_CHOICES = [
          ('Blue', 'Blue'),
          ('White', 'White')
      ]
      face_mask_color = models.CharField(max_length=25, choices=COLOR_CHOICES)
    
    そして、テーブルはこのようになります

    私は最初にどのように個々のタイプを管理者に示すことができたかについて、私に不明瞭でしたので、私は遺産をするこの方法に不満を持っていました.それは、私がDjangoの代理モデルとManager クラス.プロキシモデルを使えば、マルチテーブル継承と同じ効果を達成することができます.
    class LabelManager(models.Manager):
      def get_queryset(self):
        return super().get_queryset().filter(product_type='Label')
    
    class Label(Product):
      objects = LabelManager()
    
      class Meta:
        proxy = True
    
    class FaceMaskManager(models.Manager):
      def get_queryset(self):
        return super().get_queryset().filter(product_type='Face Mask')
    
    class FaceMask(Product):
      objects = FaceMaskManager()
    
      class Meta:
        proxy = True
    
    ジャンゴManager クラスは基本的に、オブジェクトを照会するとき、デフォルトフィルタを適用することができます.上記のコードを使用してLabel.objects.all() 得るLabel 例えばオブジェクト.The proxy = True メタオプションは、Djangoに別々のテーブルを作成しないように指示しますLabel or FaceMask オブジェクト.我々は、管理者にプロキシモデルを登録することができます、我々はちょうど我々が適切なフィールドを表示する管理者に指示することを確認する必要があります.
    @admin.register(Label)
    class LabelAdmin(admin.ModelAdmin):
      fields = ('part_number', 'price', 'dimensions')
    
    @admin.register(FaceMask)
    class FaceMaskAdmin(admin.ModelAdmin):
      fields = ('part_number', 'price', 'color')
    

    何を選んだ


    単一のテーブル継承の最大の利点は、1つのテーブルに1つのクエリを実行するだけで、必要な情報を取得する必要があります.複数テーブル継承は、スーパークラスとサブクラステーブルを結合する必要があります.我々は我々が多くの製品質問をしているということを知っていました、そして、我々はすべてのものについて心配しましたJOIN 操作は、アプリケーションを遅くします.また、私たちは当初、2種類の製品タイプのみを格納することを計画していました.我々は、これらの理由のために我々の製品データをモデル化するために、単一のテーブル継承を選びました.
    しかし、私は、マルチテーブル継承がより良い選択であったかもしれないと思い始めています.アプリケーションの要件を変更すると、いくつかの製品の種類を格納する必要があります.製品のテーブルは、私たちの製品の種類の間に共通の多くの属性がないので、空白のエントリで肥大化している.これは、製品のテーブルを混乱させると管理することは困難になり、私はそれが遅いクエリのトレードオフの価値があると思います.ありがたいことに、単一のテーブルの継承がうまく機能し、私が言及したように、我々はそれのパフォーマンスの利点を得るか.

    結論


    私は、あなたのサブクラスが一般的に多くの分野を持たないならば、マルチテーブル継承が行く方法であるということをここで学んだレッスンを仮定します.これは、はるかに効率的なメモリの場合は、空白のエントリの多くのデータベースをブロックするリスクがないです.単一のテーブル継承は、常に効率的ではありませんJOIN 操作が必要です.ほとんどのものと同様に、トレードオフがあり、正しい選択はあなたのユースケースによって異なります.