モデル

モデルは、データに関する単一の決定的な情報源です。これはあなたが保持するデータが必要とするフィールドと振る舞いについての情報を持ちます。一般的に、各モデルは単一のデータベースのテーブルに対応付けられます。

基本:

  • モデルは各々 Python のクラスであり django.db.models.Model のサブクラスです。
  • モデルの属性はそれぞれがデータベースのフィールドを表します。
  • これら全てを用いて、Django はデータベースにアクセスする自動生成された API を提供します。 クエリを作成する を参照してください。

簡単な例

次の例では first_namelast_name を持つ Person というモデルを定義しています。

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_name および last_name はこのモデルの フィールド です。各フィールドはクラスの属性として定義され、各属性はデータベースのカラムに関連付けられます。

上記の Person モデルは以下のようなデータベースのテーブルを作成します:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

技術的な注意点:

  • myapp_person というテーブル名は、いくつかのモデルのメタデータから自動的に生成されますがそれを無効化する事ができます。詳細は Table names を参照ください。
  • id フィールドは自動的に追加されますが、この処理を無効化する事ができます。自動インクリメントのプライマリーキーフィールド を参照ください。
  • この例における CREATE TABLE の SQL は PostgreSQL の文法に準拠していますが、Django は 設定ファイル 内に定義されたデータベースバックエンドに合わせた SQL を適切に生成してくれるということを覚えておくとよいでしょう。

モデルの利用

一度モデルを定義した後は、Django にこれらのモデルを 利用する 事を知らせる必要が有ります。それは設定ファイルを編集して設定値 INSTALLED_APPS に、定義した models.py を含むモジュール名を追加する事で行えます。

たとえば、アプリケーションのモデルが myapp.models モジュール内に存在する場合 (このパッケージ構造は manage.py startapp スクリプトによってアプリケーション内に構築されるものです)、INSTALLED_APPS の部分を次のように定義します。

INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

INSTALLED_APPS 内に新たなアプリケーションを追加した場合、manage.py migrate を、初回は必要に応じて manage.py makemigrations を忘れずに実行してください。

フィールド

モデルにおいて最も重要な箇所であり -- モデルにおいて唯一必須となっている箇所 -- それはそのモデルが定義するデータベースのフィールドの一覧になります。フィールドはクラスの属性として定義されます。フィールド名として cleansave あるいは delete のような models API で利用されている文字列を選び衝突しないように注意してください。

実装例:

from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

フィールドの型

モデル内の各フィールドは適切に Field クラスのインスタンスとなっている必要が有ります。Django は利用しているフィールドクラスの型に応じて以下のような挙動を決定します:

  • データベースに対して保存するデータのタイプを伝えるカラムの型です (例: INTEGERVARCHARTEXT)。
  • フォーム領域をレンダリングする際に利用するデフォルトの HTML ウィジェット (例えば <input type="text"><select>)。
  • 最低限必要とされる入力値確認、Django の管理機能と自動生成されるフォームにおいて利用されます。

Django は多くのフィールド型を内蔵しています; それらの完全な一覧は フィールドの型 で確認できます。もし Django に内蔵されたフィールドの型では実現できない機能を実装したい場合は独自のフィールドを簡単に記述できます; Writing custom model fields を参照してください。

フィールドオプション

フィールドはそれぞれあらかじめ定められたフィールド特有の引数 ( モデルのフィールド型一覧 にまとめられています) を受け取ります。例えば、CharField (およびそのサブクラス) はそのデータを保持するためにデータベース上に定義される VARCHAR 領域の長さを定義する引数 max_length を必要とします。

全てのフィールドの型で利用できる共通の引数も存在します。すべてオプションの引数です。これらの引数についてはこちらの リファレンス 内で全て説明されていますが、ここでは、特に頻繁に使われるものについて簡単な概要を説明します。

null
True の場合、Django はデータベース内に NULL として空の値を保持します。デフォルトは False です。
blank

True の場合、フィールドはブランクになることが許容されます。デフォルトは False です。

null とは挙動が異なる事に注意してください。 blank がバリデーション由来である一方、 null は完全にデータベース由来です。あるフィールドが blank=True であれば、フォームのバリデーションは空の値の入力を許容します。あるフィールドが blank=False の場合は、そのフィールドへの入力は必須となります。

choices

フィールドの選択肢として用いられる 2 値のタプルのイテラブルなオブジェクト (例えばリストやタプル) です。このオプションが渡された場合、デフォルトのフォームウィジェットが標準のテキストフィールドからセレクトボックスに変わり与えられた選択肢のみに選択を制限します。

選択肢のリストは以下のようになります。

YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
)

各タプルの1番目の要素はデータベースに保存される値です。2番目の要素はフォームウィジェットに表示される名前です。

モデルのインスタンスが与えられたとき、choices のフィールドに対して表示される値は、get_FOO_display() メソッドを用いてアクセスすることができます。たとえば、次のようになります。

from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
default
そのフィールドのデフォルト値です。このオプションには特定の値または呼び出し可能オブジェクトを渡すことができます。呼び出し可能オブジェクトの場合、新しくオブジェクトが生成される度に呼び出されます。
help_text
フォームウィジェットとともに表示される追加の「ヘルプ」テキストです。この値はフィールドがフォームとして利用されない場合でもドキュメントとして役に立ちます。
primary_key

True の場合、設定したフィールドはそのモデルの主キーとなります。

定義したモデル内でどのフィールドに対しても primary_key=True が設定されなかった場合、Django は自動的に主キーを保存するために IntegerField を追加します、つまりその主キーに対するデフォルトの動作を変更する必要がない場合はどのフィールドに対しても primary_key=True を設定する必要が有りません。

主キーとなったフィールドは読み込み専用となります。もし既存のオブジェクトの主キーの値を変更して保存する操作を行うと、既存のオブジェクトに加えて新たなオブジェクトが生成されます。以下に例を示します。

from django.db import models

class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
<QuerySet ['Apple', 'Pear']>
unique
True の場合、そのフィールドはテーブル上で一意となる制約を受けます。

繰り返しになりますが、これらは特によく利用されるフィールドのオプションの概説です。完全な説明は 共通のフィールドオプションの説明 で参照できます。

自動インクリメントのプライマリーキーフィールド

デフォルトでは、Django の各モデルは以下のフィールドを持ちます。

id = models.AutoField(primary_key=True)

これは自動連番のプライマリーキーです。

独自のプライマリーキーを指定したい場合は、いずれかのフィールドで primary_key=True を指定するだけです。Django は Field.primary_key が明示的にセットされているのを発見すると、 id 列の自動追加はされなくなります。

各モデルには、primary_key=True が必ず 1 つだけ (明示的に宣言されるか、自動的に追加されるかのどちらかで) 存在する必要があります。

冗長な (verbose) フィールド名

Each field type, except for ForeignKey, ManyToManyField and OneToOneField, takes an optional first positional argument -- a verbose name. If the verbose name isn't given, Django will automatically create it using the field's attribute name, converting underscores to spaces.

In this example, the verbose name is "person's first name":

first_name = models.CharField("person's first name", max_length=30)

In this example, the verbose name is "first name":

first_name = models.CharField(max_length=30)

ForeignKey, ManyToManyField and OneToOneField require the first argument to be a model class, so use the verbose_name keyword argument:

poll = models.ForeignKey(
    Poll,
    on_delete=models.CASCADE,
    verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    verbose_name="related place",
)

The convention is not to capitalize the first letter of the verbose_name. Django will automatically capitalize the first letter where it needs to.

リレーション

Clearly, the power of relational databases lies in relating tables to each other. Django offers ways to define the three most common types of database relationships: many-to-one, many-to-many and one-to-one.

多対一 (many-to-one) 関係

To define a many-to-one relationship, use django.db.models.ForeignKey. You use it just like any other Field type: by including it as a class attribute of your model.

ForeignKey requires a positional argument: the class to which the model is related.

For example, if a Car model has a Manufacturer -- that is, a Manufacturer makes multiple cars but each Car only has one Manufacturer -- use the following definitions:

from django.db import models

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

You can also create recursive relationships (an object with a many-to-one relationship to itself) and relationships to models not yet defined; see the model field reference for details.

It's suggested, but not required, that the name of a ForeignKey field (manufacturer in the example above) be the name of the model, lowercase. You can, of course, call the field whatever you want. For example:

class Car(models.Model):
    company_that_makes_it = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
    )
    # ...

参考

ForeignKey fields accept a number of extra arguments which are explained in the model field reference. These options help define how the relationship should work; all are optional.

For details on accessing backwards-related objects, see the Following relationships backward example.

For sample code, see the Many-to-one relationship model example.

多対多 (many-to-many) 関係

To define a many-to-many relationship, use ManyToManyField. You use it just like any other Field type: by including it as a class attribute of your model.

ManyToManyField requires a positional argument: the class to which the model is related.

For example, if a Pizza has multiple Topping objects -- that is, a Topping can be on multiple pizzas and each Pizza has multiple toppings -- here's how you'd represent that:

from django.db import models

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

As with ForeignKey, you can also create recursive relationships (an object with a many-to-many relationship to itself) and relationships to models not yet defined.

It's suggested, but not required, that the name of a ManyToManyField (toppings in the example above) be a plural describing the set of related model objects.

It doesn't matter which model has the ManyToManyField, but you should only put it in one of the models -- not both.

Generally, ManyToManyField instances should go in the object that's going to be edited on a form. In the above example, toppings is in Pizza (rather than Topping having a pizzas ManyToManyField ) because it's more natural to think about a pizza having toppings than a topping being on multiple pizzas. The way it's set up above, the Pizza form would let users select the toppings.

参考

See the Many-to-many relationship model example for a full example.

ManyToManyField fields also accept a number of extra arguments which are explained in the model field reference. These options help define how the relationship should work; all are optional.

多対多リレーションにおける追加フィールド

When you're only dealing with simple many-to-many relationships such as mixing and matching pizzas and toppings, a standard ManyToManyField is all you need. However, sometimes you may need to associate data with the relationship between two models.

For example, consider the case of an application tracking the musical groups which musicians belong to. There is a many-to-many relationship between a person and the groups of which they are a member, so you could use a ManyToManyField to represent this relationship. However, there is a lot of detail about the membership that you might want to collect, such as the date at which the person joined the group.

For these situations, Django allows you to specify the model that will be used to govern the many-to-many relationship. You can then put extra fields on the intermediate model. The intermediate model is associated with the ManyToManyField using the through argument to point to the model that will act as an intermediary. For our musician example, the code would look something like this:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

When you set up the intermediary model, you explicitly specify foreign keys to the models that are involved in the many-to-many relationship. This explicit declaration defines how the two models are related.

中間モデルにはいくつかの制約があります。

  • Your intermediate model must contain one - and only one - foreign key to the source model (this would be Group in our example), or you must explicitly specify the foreign keys Django should use for the relationship using ManyToManyField.through_fields. If you have more than one foreign key and through_fields is not specified, a validation error will be raised. A similar restriction applies to the foreign key to the target model (this would be Person in our example).
  • For a model which has a many-to-many relationship to itself through an intermediary model, two foreign keys to the same model are permitted, but they will be treated as the two (different) sides of the many-to-many relationship. If there are more than two foreign keys though, you must also specify through_fields as above, or a validation error will be raised.
  • When defining a many-to-many relationship from a model to itself, using an intermediary model, you must use symmetrical=False (see the model field reference).

Now that you have set up your ManyToManyField to use your intermediary model (Membership, in this case), you're ready to start creating some many-to-many relationships. You do this by creating instances of the intermediate model:

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>

Unlike normal many-to-many fields, you can't use add(), create(), or set() to create relationships:

>>> # The following statements will not work
>>> beatles.members.add(john)
>>> beatles.members.create(name="George Harrison")
>>> beatles.members.set([john, paul, ringo, george])

Why? You can't just create a relationship between a Person and a Group - you need to specify all the detail for the relationship required by the Membership model. The simple add, create and assignment calls don't provide a way to specify this extra detail. As a result, they are disabled for many-to-many relationships that use an intermediate model. The only way to create this type of relationship is to create instances of the intermediate model.

The remove() method is disabled for similar reasons. For example, if the custom through table defined by the intermediate model does not enforce uniqueness on the (model1, model2) pair, a remove() call would not provide enough information as to which intermediate model instance should be deleted:

>>> Membership.objects.create(person=ringo, group=beatles,
...     date_joined=date(1968, 9, 4),
...     invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This will not work because it cannot tell which membership to remove
>>> beatles.members.remove(ringo)

However, the clear() method can be used to remove all many-to-many relationships for an instance:

>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>

Once you have established the many-to-many relationships by creating instances of your intermediate model, you can issue queries. Just as with normal many-to-many relationships, you can query using the attributes of the many-to-many-related model:

# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>

As you are using an intermediate model, you can also query on its attributes:

# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
...     group__name='The Beatles',
...     membership__date_joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>

If you need to access a membership's information you may do so by directly querying the Membership model:

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

Another way to access the same information is by querying the many-to-many reverse relationship from a Person object:

>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

一対一 (one-to-one) 関係

To define a one-to-one relationship, use OneToOneField. You use it just like any other Field type: by including it as a class attribute of your model.

This is most useful on the primary key of an object when that object "extends" another object in some way.

OneToOneField requires a positional argument: the class to which the model is related.

For example, if you were building a database of "places", you would build pretty standard stuff such as address, phone number, etc. in the database. Then, if you wanted to build a database of restaurants on top of the places, instead of repeating yourself and replicating those fields in the Restaurant model, you could make Restaurant have a OneToOneField to Place (because a restaurant "is a" place; in fact, to handle this you'd typically use inheritance, which involves an implicit one-to-one relation).

As with ForeignKey, a recursive relationship can be defined and references to as-yet undefined models can be made.

参考

See the One-to-one relationship model example for a full example.

OneToOneField fields also accept an optional parent_link argument.

OneToOneField classes used to automatically become the primary key on a model. This is no longer true (although you can manually pass in the primary_key argument if you like). Thus, it's now possible to have multiple fields of type OneToOneField on a single model.

Models across files

あるアプリのモデルを別のアプリのモデルに関係づけることは全く問題ありません。そのためには、モデルが定義されているファイルの先頭で、関連するモデルをインポートします。そして、単に必要な他のモデルクラスを参照するだけで大丈夫です。たとえば、次のようになります。

from django.db import models
from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(
        ZipCode,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

フィールド名の制約

Django がフィールド名に課している制約は、次のたった2つです。

  1. フィールド名には Python の予約語が使えない。もし使用した場合、Python の構文エラーが起こります。たとえば、次のようになります。

    class Example(models.Model):
        pass = models.IntegerField() # 'pass' is a reserved word!
    
  2. フィールド名に2文字以上連続するアンダースコアを使用できない。これは、Django のクエリルックアップの構文のためです。たとえば、次のようになります。

    class Example(models.Model):
        foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
    

しかし、これらの制約を回避する手段はあります。なぜなら、フィールド名は必ずしもデータベースのカラム名と一致する必要はないからです。詳細については db_column オプションをご覧ください。

SQL reserved words, such as join, where or select, are allowed as model field names, because Django escapes all database table names and column names in every underlying SQL query. It uses the quoting syntax of your particular database engine.

カスタムのフィールドタイプ

If one of the existing model fields cannot be used to fit your purposes, or if you wish to take advantage of some less common database column types, you can create your own field class. Full coverage of creating your own fields is provided in Writing custom model fields.

Meta オプション

Give your model metadata by using an inner class Meta, like so:

from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

Model metadata is "anything that's not a field", such as ordering options (ordering), database table name (db_table), or human-readable singular and plural names (verbose_name and verbose_name_plural). None are required, and adding class Meta to a model is completely optional.

A complete list of all possible Meta options can be found in the model option reference.

Model attributes

objects
The most important attribute of a model is the Manager. It's the interface through which database query operations are provided to Django models and is used to retrieve the instances from the database. If no custom Manager is defined, the default name is objects. Managers are only accessible via model classes, not the model instances.

Model methods

Define custom methods on a model to add custom "row-level" functionality to your objects. Whereas Manager methods are intended to do "table-wide" things, model methods should act on a particular model instance.

This is a valuable technique for keeping business logic in one place -- the model.

For example, this model has a few custom methods:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"

    @property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)

The last method in this example is a property.

The model instance reference has a complete list of methods automatically given to each model. You can override most of these -- see overriding predefined model methods, below -- but there are a couple that you'll almost always want to define:

__str__()

A Python "magic method" that returns a string representation of any object. This is what Python and Django will use whenever a model instance needs to be coerced and displayed as a plain string. Most notably, this happens when you display an object in an interactive console or in the admin.

You'll always want to define this method; the default isn't very helpful at all.

get_absolute_url()

This tells Django how to calculate the URL for an object. Django uses this in its admin interface, and any time it needs to figure out a URL for an object.

Any object that has a URL that uniquely identifies it should define this method.

Overriding predefined model methods

There's another set of model methods that encapsulate a bunch of database behavior that you'll want to customize. In particular you'll often want to change the way save() and delete() work.

You're free to override these methods (and any other model method) to alter behavior.

A classic use-case for overriding the built-in methods is if you want something to happen whenever you save an object. For example (see save() for documentation of the parameters it accepts):

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super().save(*args, **kwargs)  # Call the "real" save() method.
        do_something_else()

You can also prevent saving:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        if self.name == "Yoko Ono's blog":
            return # Yoko shall never have her own blog!
        else:
            super().save(*args, **kwargs)  # Call the "real" save() method.

It's important to remember to call the superclass method -- that's that super().save(*args, **kwargs) business -- to ensure that the object still gets saved into the database. If you forget to call the superclass method, the default behavior won't happen and the database won't get touched.

It's also important that you pass through the arguments that can be passed to the model method -- that's what the *args, **kwargs bit does. Django will, from time to time, extend the capabilities of built-in model methods, adding new arguments. If you use *args, **kwargs in your method definitions, you are guaranteed that your code will automatically support those arguments when they are added.

Overridden model methods are not called on bulk operations

Note that the delete() method for an object is not necessarily called when deleting objects in bulk using a QuerySet or as a result of a cascading delete. To ensure customized delete logic gets executed, you can use pre_delete and/or post_delete signals.

Unfortunately, there isn't a workaround when creating or updating objects in bulk, since none of save(), pre_save, and post_save are called.

Executing custom SQL

Another common pattern is writing custom SQL statements in model methods and module-level methods. For more details on using raw SQL, see the documentation on using raw SQL.

モデルの継承

Django におけるモデルの継承は Python における普通のクラスの継承とはかなり異なった方法で行われます、しかし最も最初の段階で基本となる考えは共通しています。それはその基底クラスが django.db.models.Model のサブクラスでなければならないという事です。

下すべき決定事項は親モデルを (保持するデータベースのテーブルと共に) 独自の権限を持ったモデルとするか、それとも親モデルは単に子モデルを通じてのみ参照できる共通情報を持っているだけの存在とするかという事だけです。

Django において可能な継承には 3 つの方式が有ります。

  1. 往々にして、子モデルそれぞれに対して一々定義し直さないで済ませたい情報を保持するためだけに親クラスを用いようとするでしょう。そのクラスは単体で用いられることはありません、抽象基底クラス こそあなたが必要とする物でしょう。
  2. もし既存のモデル (完全に別のアプリケーション等から取得した) のサブクラスを作成していてモデルそれぞれにデータベース上のテーブルを定義したい場合、 複数テーブルの継承 を利用するとよいでしょう。
  3. 最後に、もしあるモデルの Python レベルでの振る舞いを、そのモデルのフィールドをいかなる形でも変更せずに修正したい場合は プロキシモデル を利用できます。

抽象基底クラス

Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class. This model will then not be used to create any database table. Instead, when it is used as a base class for other models, its fields will be added to those of the child class.

実装例:

from django.db import models

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

Student モデルは nameage そして home_group の 3 つのフィールドを持つことになります。 CommonInfo モデルは抽象基底クラスであるため、通常の Django モデルとしては利用できません。データベース上にテーブルを生成したりマネージャを持ったりせず、そしてインスタンス化されたり直接値を保存する事もできません。

Fields inherited from abstract base classes can be overridden with another field or value, or be removed with None.

大抵の状況において、このモデル継承の方式がまさにあなたが必要としていた物となるでしょう。この方式では、データベースの領域では子モデル毎に一つずつだけのテーブルを作る事しかできませんが、共通で持ちたい情報を Python の水準で分離できます。

Meta 継承

抽象基底クラスを作成した際、Django は基底クラスから利用できる Meta インナークラスを全て子クラスの属性値にします。もしある子クラスが自身の Meta クラスを定義しなければ、親クラスの Meta クラスを継承します。もし子クラスが親の Meta クラスを拡張したい場合は、継承したクラスのサブクラス化が可能です。以下のようになります:

from django.db import models

class CommonInfo(models.Model):
    # ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    # ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'

Django は抽象基底クラスの Meta クラスに対して一つの変更を施します: Meta 属性を子クラスに導入する前に、 abstract=False を設定します。これは抽象基底クラスを継承した子クラスは自身を自動的に抽象基底クラスにはしない事を意味します。言うまでもなく、他の抽象基底クラスを継承した抽象基底クラスを作る事はできます。継承する都度 abstract=True を設定する事を忘れなければ良いだけです。

抽象基底クラスの Meta クラスに持っても意味をなさない属性がいくつか存在します。例えば、属性値 db_table を持つことは継承した子クラス (の中で自身の Meta を定義していない物) が全て同じデータベース上のテーブルを利用する事を意味しますが、それは期待する動作と明らかに異なるでしょう。

複数テーブルの継承

Django がサポートするもう一つのモデル継承は、ヒエラルキー内の各モデルすべてが、それ自体モデルであるような場合です。それぞれのモデルはデータベース上のテーブルに対応しており、個別にクエリーを作成したり、テーブルの作成ができます。継承の関係によって、(自動的に作成される OneToOneField を通して) 子モデルとその親モデルとの間にリンクが作られます。この継承を利用すると、たとえば、次のように書くことができます。

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

データはデータベースの異なるテーブルに存在しますが、 Place のフィールドは全て Restaurant でも利用できます。以下は共に処理可能です:

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

Place を取得すれば Restaurant も取得することになり、小文字のモデル名を用いることで Place オブジェクトから Restaurant オブジェクトを取得できます:

>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>

しかしながら、上の例において pRestaurant オブジェクト ではない (継承を用いず Place オブジェクトが直接作成されたもしくは他のクラスの親であった)場合、 p.restaurant を参照すると Restaurant.DoesNotExist 例外を創出します。

The automatically-created OneToOneField on Restaurant that links it to Place looks like this:

place_ptr = models.OneToOneField(
    Place, on_delete=models.CASCADE,
    parent_link=True,
)

You can override that field by declaring your own OneToOneField with parent_link=True on Restaurant.

``メタクラス``と複数テーブルの継承

複数のテーブルを継承する状況では、子クラスにとって親クラスの Meta クラスを継承する事には意味が有りません。全ての Meta オプションは既に親クラスにおいて適用されておりそれを再度定義する事は矛盾した挙動を示すことに繋がります(これは基礎となるクラスの存在を当然の機能としては期待できない抽象基底クラスと対照的です)。

そのため子モデルは親モデルの Meta クラスへのアクセスを持ちません。しかし、限られた状況において子モデルが親モデルから挙動を継承する場合が有ります:もし子モデルが ordering 属性もしくは get_latest_by 属性を設定しなかった場合、これらを親モデルから継承します。

もし親モデルが ordering 属性を持っており子モデルに既定のデータ並び順を持たせたくない場合、明示的に無効化する事ができます:

class ChildModel(ParentModel):
    # ...
    class Meta:
        # Remove parent's ordering effect
        ordering = []

継承と関係の逆引き

複数テーブルの継承は親モデルと子モデルの関連付けに暗黙的な OneToOneField を利用するため、先の例で示されたように、親から子に情報を移動させることは可能です。しかしその関連付けには ForeignKey および ManyToManyField に付与されたデフォルトの related_name の値を利用します。もしこの種類の関連付けを親モデルのサブクラスに対して用いる場合、 related_name 属性を各フィールドに 定義しなければなりません 。もし設定を忘れれば、Django はバリデーション例外を送出します。

例として、上で用いた Place クラスを再度用いて、また別のサブクラスを ManyToManyField によって作成してみましょう:

class Supplier(Place):
    customers = models.ManyToManyField(Place)

この処理の結果はエラーとなります:

Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.

HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.

customers フィールドに related_name を追加する事でこのエラーを解消できます: models.ManyToManyField(Place, related_name='provider')

プロキシモデル

複数テーブルの継承 を利用しているとき、あるモデルのサブクラス毎に新たなデータベースのテーブルが作成されます。これは基底クラスに存在しない追加のデータフィールドをサブクラスが保存する場所が必要なため、通常必要とされる動作です。しかしながら、モデルの Python 上の振る舞いを変更したい事も有ります -- デフォルトのマネージャーを変更したり、新たなメソッドを追加したりする場合です。

プロキシモデルによる継承が利用できるのはこういった状況です:オリジナルのモデルのための プロキシ を作るのです。プロキシモデルで通してインスタンスを作成、削除、更新して (プロキシされない) オリジナルのモデルを利用しているかのようにデータを保存できます。プロキシを利用した場合に生じる違いは、オリジナルのモデルに変更を加えることなく、デフォルトのモデルのソート順やマネージャーの設定をプロキシ上で変更できるという点です。

プロキシモデルは通常のモデルと同様に定義されます。Django にそれがプロキシモデルである事を伝えるには Meta クラスの proxy 属性値を True にします。

例として、Person モデルにメソッドを追加すると仮定します。以下のように記述することで実現できます:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

MyPerson クラスはその親である Person クラス同様に同じデータベースのテーブルを操作しています。特に、Person の新たなインスタンスは全て MyPerson を通じてアクセス可能であり、逆も同様です:

>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>

あるモデルに対して並び順の既定値を定義したい場合もプロキシモデルを利用できます。普段は Person モデルの並び順を特に指定していないが、定期的に last_name 属性に基づいて並び替える場合にプロキシを利用できます。とても簡単に利用できます:

class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True

これで通常の Person に対する問い合わせでは特に結果は並び替えられず OrderedPerson に対する問い合わせでは last_name に基づいて並び替えられます。

プロキシモデルは Meta の属性値を 通常のモデルと同様に 継承します。

QuerySets はリクエストされたモデルを返し続ける

Person オブジェクトに対して問い合わせた場合に Django に、あえて言えば、 MyPerson オブジェクトを返させる事はできません。プロキシオブジェクトのポイントはオリジナルの Person に依存しているコードはそちらを使いつつ、新たに含んだ (他のコードがどんな形であれ依存しない) 拡張を使うコードを書けるという点です。 Person モデル (や他の全てのモデル) を新たに作った何物かに置き換える方法ではありません。

基底クラスの制限

プロキシモデルは厳格に一つの抽象的でないモデルクラスを継承しなければなりません。複数の非抽象クラスを、異なるデータベースのテーブル上の列と列を接続しない、プロキシモデルとして継承する事はできません。プロキシモデルは、いかなるモデルのフィールドも定義 しない 、抽象モデルクラスをいくらでも継承できます。プロキシモデルはまた共通の非抽象な親クラスを持つプロキシモデルをいくらでも継承できます。

プロキシモデルマネージャー

プロキシモデル上にモデルのマネージャーを定義しない場合、そのプロキシモデルはマネージャーを親のモデルから継承します。プロキシモデル上にマネージャーを定義する場合、親クラス上のマネージャーで定義されて利用可能な物が有ったとしても、そのマネージャーがデフォルトになります。

前節の例を使うと、 Person モデルに問い合わせる場合は以下のようにデフォルトマネージャーを変更して用いることができます:

from django.db import models

class NewManager(models.Manager):
    # ...
    pass

class MyPerson(Person):
    objects = NewManager()

    class Meta:
        proxy = True

既存の物を置き換えることなく、プロキシモデルに新たなマネージャーを追加したい場合は、 カスタムマネージャー の説明に記述されたテクニックを用いることができます: 新たに実装したマネージャーを含んだ基底クラスを作成し、これを元々の基底クラスに続けて多重継承してください:

# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
    secondary = NewManager()

    class Meta:
        abstract = True

class MyPerson(Person, ExtraManagers):
    class Meta:
        proxy = True

上記の操作が頻繁に必要となる事は無いでしょうが、このような実装も可能なのです。

プロキシモデルの継承と管理対象外モデルの違い

プロキシモデルの継承は、あるモデルの Meta クラスにおける managed 属性を用いて Django に管理されないモデルを作成するのにかなり似ています。

Meta.db_table を入念に設定する事で既に存在するモデルとそれに対応する Python メソッドを隠ぺいする管理対象外モデルを作成できます。しかしながら、この操作は変更を加えた際に定義を複製して同期する際にとても冗長で変更に弱い物となります。

一方、プロキシモデルはプロキシするモデルと全く同じように振る舞うことを企図しています。それらのモデルは直接フィールドとマネージャーを継承しているため、常に親モデルに同期しています。

原則としては:

  1. もし既存のモデルやデータベースのテーブルを反映したいが元となるテーブルのカラムの全てを必要とする訳ではない場合は、 Meta.managed=False を利用してください。このオプションはデータベースのビューやテーブルを Django の管理下に置きたくない場合に通常有用となります。
  2. もしあるモデルの Python の箇所の振る舞いだけを変更したいが、元となるモデルと同じフィールドを保持したい場合は Meta.proxy=True を使用してください。この設定はデータ保存時にプロキシモデルが、元となるモデル側の保管構造の完全な複製となるように諸々を設定します。

多重継承

Python におけるサブクラス化と同様に、 Django のモデルは複数の親モデルを継承する事ができます。Python で通常用いられる名前解決の規則が適用されることを念頭に置いてください。特定の名称のオブジェクト (例えば Meta クラスを持っていた場合、最初に定義されたモデルのクラスが利用され、それ以外は無視されます。

通常は複数の親モデルを継承する必要はありません。この機能が有用な主な状況はクラスの "ミックスイン" 、ミックスイン用クラスを継承する全てのクラスに特別なフィールドやメソッドを追加させる技術、を利用する場合です。情報の特定の箇所がどこに由来するか突き止めるため奮闘しなくて済むように、継承の依存関係を可能な限り簡単で直観的な状態で維持するようにして下さい。

共通の id 主キーフィールドを持った複数のモデルを継承すると例外が送出されることに注意してください。多重継承を正常に使うために、基底モデル内で明示的に AutoField を利用できます。

class Article(models.Model):
    article_id = models.AutoField(primary_key=True)
    ...

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    ...

class BookReview(Book, Article):
    pass

Or use a common ancestor to hold the AutoField. This requires using an explicit OneToOneField from each parent model to the common ancestor to avoid a clash between the fields that are automatically generated and inherited by the child:

class Piece(models.Model):
    pass

class Article(Piece):
    article_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
    ...

class Book(Piece):
    book_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
    ...

class BookReview(Book, Article):
    pass

フィールド名の "隠ぺい" は許可されない

通常の Python のクラス継承では、親クラスから継承したあらゆる属性を子クラスがオーバーライドできます。Django では、この挙動は通常モデルのフィールドに対して許容されません。もし非抽象なモデルの基底クラスが author フィールドを持っていたとしたら、そのモデルの基底クラスを継承したあらゆるモデルクラス内では author という名称の属性値を別のモデルのフィールドや属性として定義できなくなります。

この制約は抽象モデルを継承したモデルのフィールドに対しては適用されません。こういったフィールドは別のフィールドや値によってオーバーライドされるか field_name = None を設定することで削除する事ができます:

警告

モデルマネージャーは抽象基底クラスより継承されます。継承された Manager から参照される継承されたフィールドをオーバーライドするとちょっとしたバグを発生させる事が有ります。詳細は カスタムマネージャーとモデルの継承 を参照ください。

注釈

ある種のフィールドはモデル上に特別な属性を定義します、例えば ForeignKey_id がフィールド名に付与された属性を、外部モデル上の related_namerelated_query_name と同じように定義します。

これらの特別な属性はその属性を定義しているフィールドが、もはやその特別な属性を定義しなくなるように、変更もしくは削除されなければオーバーライドできません。

親モデルのフィールドをオーバーライドする事はインスタンス (どのフィールドが初期化されるか Model.__init__ に定義している) の初期化やシリアライズ時の問題につながります。これらは通常の Python のクラス継承で全く同様に扱う事が求められていない機能であるため、 Django のモデル継承と Python のクラス継承のこうした差異は恣意的な設計による物ではありません。

この制限は Field のインスタンスである属性に対してのみ適用されます。通常の Python の属性は望むままにオーバーライドできます。またその制約は Python が解析する際の属性の名称にのみ影響します:もしデータベースのカラム名を手動で設定したい場合、複数テーブルの継承 (2 つの異なるデータベースのテーブルに保持されているカラム) を表現する際に子と祖先のモデルで同じカラム名を設定できます。

もし祖先モデルのいずれかに定義されたモデルのフィールドをオーバーライドした場合 Django は FieldError 例外を送出します。

パッケージ化したモデルを扱う

manage.py startapp コマンドを実行することで models.py ファイルを含むアプリケーション構造が作成されます。多数のモデルが存在する場合、それらを別のファイルとして管理するのが良いかもしれません。

それを行うため、 models パッケージを作成します。 models.py を削除してモデルを保持するための __init__.py ファイルを持つ myapp/models/ ディレクトリを作成します。作成した __init__.py でモデルをインポートする必要が有ります。

例えば、 organic.py および synthetic.pymodels ディレクトリ内に持っている場合:

myapp/models/__init__.py
from .organic import Person
from .synthetic import Robot

from .models import * という記述を用いずに各モデルを明示的にインポートする方法には名前空間を汚染しない、可読性を向上させる、コード解析ツールを有用な状態に保つという利点が有ります。

参考

モデルの手引き
Covers all the model related APIs including model fields, related objects, and QuerySet.
Back to Top