モデル¶
モデルは、データに関する唯一かつ決定的な情報源です。あなたが保持するデータが必要とするフィールドとその動作を定義します。一般的に、各モデルは単一のデータベースのテーブルに対応付けられます。
基本:
- モデルは各々 Python のクラスであり
django.db.models.Model
のサブクラスです。 - モデルの属性はそれぞれがデータベースのフィールドを表します。
- これら全てを用いて、Django はデータベースにアクセスする自動生成された API を提供します。 クエリを作成する を参照してください。
簡単な例¶
次の例では first_name
と last_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" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
技術的な注意点:
myapp_person
というテーブル名は、いくつかのモデルのメタデータから自動的に生成されますが、それを無効化することもできます。詳細は テーブル名 を参照ください。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
を忘れずに実行してください。
フィールド¶
モデルにおいて最も重要な箇所であり -- モデルにおいて唯一必須となっている箇所 -- それはそのモデルが定義するデータベースのフィールドの一覧です。フィールドはクラスの属性として定義されます。clean
、save
あるいは delete
のようなフィールド名は、モデル 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 は、利用しているフィールドクラスの型に応じて以下のような挙動を決定します。
- データベースに対して保存するデータのタイプを伝えるカラムの型です (例:
INTEGER
、VARCHAR
、TEXT
)。 - フォーム領域をレンダリングする際に利用するデフォルトの HTML ウィジェット (例えば
<input type="text">
、<select>
)。 - 最低限必要とされる入力値確認、Django の管理機能と自動生成されるフォームにおいて利用されます。
Django は多くのフィールド型を内蔵しています; それらの完全な一覧は フィールドの型 で確認できます。もし Django に内蔵されたフィールドの型では実現できない機能を実装したい場合は独自のフィールドを簡単に記述できます; カスタムのモデルフィールドを作成する を参照してください。
フィールドオプション¶
フィールドはそれぞれあらかじめ定められたフィールド特有の引数 ( モデルのフィールド型一覧 にまとめられています) を受け取ります。例えば、CharField
(およびそのサブクラス) はそのデータを保持するためにデータベース上に定義される VARCHAR
領域の長さを定義する引数 max_length
を必要とします。
全てのフィールドの型で利用できる共通の引数も存在します。すべてオプションの引数です。これらの引数についてはこちらの リファレンス 内で全て説明されていますが、ここでは、特に頻繁に使われるものについて簡単な概要を説明します。
null
True
の場合、Django はデータベース内にNULL
として空の値を保持します。デフォルトはFalse
です。blank
True
の場合、フィールドはブランクになることが許容されます。デフォルトはFalse
です。null
とは挙動が異なる事に注意してください。blank
がバリデーション由来である一方、null
は完全にデータベース由来です。あるフィールドがblank=True
であれば、フォームのバリデーションは空の値の入力を許容します。あるフィールドがblank=False
の場合は、そのフィールドへの入力は必須となります。choices
2 値タプルの sequence, もしくは mapping, 列挙型, または呼び出し可能オブジェクト (引数を指定せず、前述の形式のいずれかを返す) をこのフィールドの選択肢として使用します。これが指定された場合、デフォルトのフォームウィジェットは標準のテキストフィールドではなくセレクトボックスとなり、選択肢は指定されたものに制限されます。
選択肢のリストは以下のようになります。
YEAR_IN_SCHOOL_CHOICES = [ ("FR", "Freshman"), ("SO", "Sophomore"), ("JR", "Junior"), ("SR", "Senior"), ("GR", "Graduate"), ]
注釈
choices
の順番を変更すると、変更のたびに新しいマイグレーションが生成されます。各タプルの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'
列挙型クラスを用いて、
choices
を簡潔に定義することもできます。from django.db import models class Runner(models.Model): MedalType = models.TextChoices("MedalType", "GOLD SILVER BRONZE") name = models.CharField(max_length=60) medal = models.CharField(blank=True, choices=MedalType, max_length=10)
さらに他の例は model field リファレンス で見ることができます。
Changed in Django 5.0:マッピングと callable のサポートが追加されました。
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 は各モデルに、 AppConfig.default_auto_field
でアプリごとに指定した型、または DEFAULT_AUTO_FIELD
設定でグローバルに指定した型の自動インクリメントの主キーを付与します。例えば:
id = models.BigAutoField(primary_key=True)
独自の主キーを設定したい場合は、いずれかのフィールドで primary_key=True
を設定してください。そのようにして明示的に Field.primary_key
が設定されている場合、Djangoは自動的に id
カラムを追加しません。
各モデルには、primary_key=True
が必ず 1 つだけ (明示的に宣言されるか、自動的に追加されるかのどちらかで) 存在する必要があります。
詳細な (verbose) フィールド名¶
ForeignKey
、ManyToManyField
、OneToOneField
の 3 つを除いた各フィールドは、任意の第 1 引数を取ります -- 詳細な (verbose) 名前です。詳細な名前を与えない場合、Django は自動的にフィールドの属性名のアンダースコアをスペースに変換したものを生成します。
以下の例では、詳細な名前は "person's first name"
です:
first_name = models.CharField("person's first name", max_length=30)
以下の例では、詳細な名前は "first name"
です:
first_name = models.CharField(max_length=30)
ForeignKey
、ManyToManyField
、OneToOneField
の 3 つは最初の引数にモデルクラスを必要とするので、verbose_name
キーワード引数を使用してください:
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",
)
慣習的に、verbose_name
の最初の文字は大文字にしません。必要に応じて Django が自動的に変換します。
リレーション¶
リレーショナルデータベースの強力さがテーブル同士の関係によって決まることは疑いがありません。Dango では、最も一般的な 3 つのデータベースリレーションシップを定義しています: 多対 1、多対多、1 対 1 です。
多対一 (many-to-one) 関係¶
多対 1 の関係を定義するには、django.db.models.ForeignKey
を使用してください。 使い方は他の Field
型と同じです: モデルのクラス属性として含めてください。
ForeignKey
には位置引数が必要です: モデルと関係させるクラスです。
たとえば、Car
モデル Manufacturer
を持っている場合、つまり、 Manufacturer
は複数の car を作る一方で Car
は 1 つしか Manufacturer
を持たない場合、以下の定義を使用してください。
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...
再帰的なリレーションシップ (自分自身に対する多対 1 の関係を持つオブジェクト) や まだ定義されていないモデルへのリレーションシップ を作成することもできます。詳細は モデルフィールドのリファレンス を参照してください。
必須ではありませんが、ForeignKey
フィールド名 (上記の例では manufacturer
) はモデル名を小文字にしたものをおすすめします。もちろん、どんなフィールドでも呼び出すことができます。例えば、以下のようなフィールド名にすることも可能です。
class Car(models.Model):
company_that_makes_it = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
)
# ...
参考
ForeignKey
フィールドは多数の追加的な引数を受け入れます。これらは モデルフィールドのリファレンス にて説明しています。これらの引数は、リレーションシップがどのように動作すべきかを定義するのに役立ちます。追加の引数はすべて任意です。
逆方向のリレーションを持つオブジェクトへのアクセスの詳細については、下記の 逆方向のリレーションシップの例 を参照してください。
サンプルのコードは、多対多のリレーションシップモデルの例 を参照してください。
多対多 (many-to-many) 関係¶
多対多の関係を定義するには、django.db.models.ManyToManyField
を使用してください。 使い方は他の Field
型と同じです: モデルのクラス属性として含めてください。
ManyToManyField
には位置引数が必要です: モデルと関係させるクラスです。
たとえば、Pizza
が複数の Topping
オブジェクトを持っている(そして Topping
は複数の pizza に載ることができ Pizza
は複数の topping を持つ)場合、以下のように表現できます:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
ForeignKey
と同様に、再帰的なリレーションシップ (自分自身に対する多対多のリレーションシップを持つオブジェクト) や まだ定義されていないモデルへのリレーションシップ を作成することもできます。
必須ではありませんが、ManyToManyField
フィールド (上記の例では toppings
) はリレーション先のモデルオブジェクトの複数形が推奨されています。
どちらのフィールドに ManyToManyField
を定義しても構いませんが、片方のフィールドのみに定義するようにしてください -- 両方ではありません。
一般的に、 ManyToManyField
インスタンスは、フォームで編集されるオブジェクトの側にあります。上の例では、 toppings
は Pizza
の中にあります (Topping
が pizzas
の ManyToManyField
を持つとするよりはそのほうがいいでしょう)。というのは、ピザが複数のトッピングを持つほうが、トッピングが複数のピザの上にあるというよりも自然だからです。このようにすることで、ユーザは Pizza
フォームでトッピングを選ぶことができるようになります。
参考
完全な実装例は 多対多のリレーションシップモデルの例 を参照してください。
ManyToManyField
フィールドは多数の追加的な引数を受け入れます。これらは モデルフィールドのリファレンス にて説明しています。これらの引数は、リレーションシップがどのように動作すべきかを定義するのに役立ちます。引数はすべて任意です。
多対多リレーションにおける追加フィールド¶
ピザとトッピングを組み合わせる程度の多対多リレーションを扱うのであれば、標準の ManyToManyField
で十分でしょう。しかし、2 つのモデルのリレーションに他のデータを付加したくなることもあります。
たとえば、ミュージシャンが所属する音楽グループを追跡するアプリについて考えてみましょう。ミュージシャンとグループの間には多対多の関係があるので、この関係を表すのに ManyToManyField
が使えます。しかし、ある人がそのグループに加入した日などといった多くの詳細情報も集めたいとします。
このような場合、 Django ではそのような多対多リレーションを規定するのに使われるモデルを指定できます。そうすることで、中間モデルに追加のフィールドを配置できます。中間モデルは、 through
引数で中間として振る舞うモデルを指定することで、 ManyToManyField
に紐付けることができます。ミュージシャンの例では、コードはこのようになるでしょう:
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)
中間モデルを指定する場合は、多対多リレーションに参加するモデルに対する外部キーを明示的に指定します。明示的に指定することで、ふたつのモデルがどのように関係するかが定義されます。
中間モデルにはいくつかの制約があります。
- 中間モデルには、ソースモデル(この例では
Group
)への外部キーが ただ1つだけ 含まれている必要があります。または、Djangoがリレーションシップに使用すべき外部キーをManyToManyField.through_fields
を使用して明示的に指定する必要があります。もし複数の外部キーがあり、through_fields
が指定されていない場合、バリデーションエラーが発生します。ターゲットモデル(この例ではPerson
)への外部キーにも同様の制限が適用されます。 - 中間モデルを介して自分自身と多対多のリレーションシップを持つモデルの場合、同じモデルに対する2つの外部キーは許可されますが、多対多のリレーションシップの2つの(異なる)側として扱われます。しかし、2つ以上の外部キーがある場合は、上記のように
through_fields
も指定しなければなりません。
これで ManyToManyField
が中間モデル (この場合は Membership
) を使うように設定できたので、多対多のリレーションシップを作成する準備ができました。これを行うには、中間モデルのインスタンスを作成します:
>>> 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>]>
また、 add()
や create()
、 set()
を使ってリレーションシップを作成することもできます(through_defaults
フィールドの指定は必須です):
>>> beatles.members.add(john, through_defaults={"date_joined": date(1960, 8, 1)})
>>> beatles.members.create(
... name="George Harrison", through_defaults={"date_joined": date(1960, 8, 1)}
... )
>>> beatles.members.set(
... [john, paul, ringo, george], through_defaults={"date_joined": date(1960, 8, 1)}
... )
中間モデルのインスタンスを直接作成したいこともあるでしょう。
中間モデルで定義されたカスタムスルーテーブルが (model1, model2)
ペアの一意性を強制せず、複数の値を許容する場合、 remove()
呼び出しは全ての中間モデルインスタンスを削除します:
>>> 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 deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
clear()
メソッドを使用すると、インスタンスの多対多の関係を全て削除できます:
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>
多対多のリレーションシップを確立したら、クエリを発行できます。通常の多対多のリレーションシップと同様に、多対多のリレーション先モデルの属性を使用してクエリを発行できます:
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith="Paul")
<QuerySet [<Group: The Beatles>]>
中間モデルを使用しているので、その属性に対してクエリを実行することもできます:
# 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]>
会員情報にアクセスする必要がある場合は、直接 Membership
モデルにクエリを発行してください:
>>> 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.'
同じ情報にアクセスするもう一つの方法は、 Person
オブジェクトから 多対多の逆リレーションシップ を問い合わせることです:
>>> 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) 関係¶
1対1のリレーションを定義するには、OneToOneField
を使用します。単純に他の Field
型と同じようにして、モデルのクラス属性に設定できます。
あるオブジェクトが他のオブジェクトを何らかの方法で「拡張 (extends)」しているとき、オブジェクトの主キーに設定するのが最も便利です。
OneToOneField
には1つの位置引数として、モデルと関係させるクラスを指定する必要があります。
例えば、"場所" のデータベースを構築する場合、住所や電話番号などの標準的なものをデータベースに構築します。そして、その場所の上にレストランのデータベースを構築したい場合、 Restaurant
モデルにこれらのフィールドを複製する代わりに、 Restaurant
に Place
に対する OneToOneField
を持たせることができます (レストランは「場所」だからです。実際、これを扱うには、暗黙の一対一関係を含む 継承 を使うのが一般的です)。
ForeignKey
と同様に、再帰的なリレーションシップ や まだ定義されていないモデルへの参照 を作成することもできます。
参考
完全な実装例は、1対1リレーションモデルの例 を参照してください。
OneToOneField
フィールドには、オプションの parent_link
引数を指定することもできます。
以前は OneToOneField
クラスが自動的にモデルの主キーになっていました。しかし、現在はそうではありません (手動で primary_key
引数を渡すこともできます)。このため、1つのモデルに OneToOneField
型のフィールドを複数持つことができるようになりました。
ファイルを横断したモデル¶
モデルを他のアプリのモデルと関連付けることも全く問題ありません。これを行うには、モデルが定義されているファイルの先頭で、関連するモデルをインポートします。そして、必要なところで他のモデルクラスを参照してください。たとえば、次のようにします:
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 はモデルのフィールド名にいくつかの制約を課しています。
フィールド名はPythonの予約語にはできません。Pythonの構文エラーになるからです。たとえば次のようなものです:
class Example(models.Model): pass = models.IntegerField() # 'pass' is a reserved word!
フィールド名には連続したアンダースコアを2つ以上含めることはできません。これはDjangoのクエリルックアップ構文のためです。たとえば次のようなものです:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
同様の理由で、フィールド名の最後をアンダースコアにすることはできません。
しかし、これらの制約を回避する手段はあります。なぜなら、フィールド名は必ずしもデータベースのカラム名と一致する必要はないからです。詳細については db_column
オプションをご覧ください。
ただし、join
、where
、select
などの SQL の予約語はモデルのフィールド名に使用できます。これは、Django がすべての SQL クエリに対して、データベースデーブル名とカラム名のエスケープを行っているためです。エスケープ処理では、使用しているデータベースエンジンのクオート構文を利用しています。
カスタムのフィールドタイプ¶
既存のモデルフィールドが目的に適わない場合、あるいは一般的ではないデータベースカラム型を利用したいと考えチエル場合、独自のフィールドクラスを作成できます。自分自身のフィールドを作成する方法は、カスタムのモデルフィールドを作成する で提供しています。
Meta
オプション¶
内側に class Meta
というクラスを定義することで、モデルのメタデータを設定できます。
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
モデルのメタデータには「フィールド以外のすべての事項」が設定できます。例えば、並び替えオプション (ordering
)、データベースのテーブル名 (db_table
)、人が読むための単数形と複数形の名前 (verbose_name
と verbose_name_plural
) などです。必須のものはなく、class Meta
のモデルへの追加は完全にオプションです。
Meta
に指定できるオプションの完全なリストは、モデルオプションリファレンス で確認できます。
モデルの属性¶
objects
- モデルの最も重要な属性は
Manager
です。これは、Django のモデルにデータベースクエリの操作を渡すインターフェースで、データベースから インスタンスを取り出す ために使われます。Manager
が定義されていない場合、デフォルトの名前はobjects
となります。マネージャはモデルクラスを通じてのみアクセスできます。インスタンスからはアクセスできません。
モデルのメソッド¶
オブジェクトに独自の "行レベルの" 機能を追加するには、カスタムのメソッドを定義してください。Manager
メソッドは "テーブル単位の" 操作をするように意図されており、モデルメソッドは特定のモデルインスタンス上で動作します。
これは、ビジネスロジックを 1 つの場所(モデル)で管理するための重要なテクニックです。
例えば、以下のモデルはいくつかのカスタムメソッドを持ちます:
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 f"{self.first_name} {self.last_name}"
例にある最後のメソッドは property です。
モデルインスタンスのリファレンス には各モデルに自動的に与えられる メソッド の完全なリストがあります。ほとんどはオーバーライドできます(後述の overriding predefined model methods を参照してください)が、特によく定義することになるものがあります:
__str__()
Python の "魔法のメソッド" で、オブジェクトを表す文字列を返しま。 Python と Django は、モデルのインスタンスを文字列で表示する必要に迫られたときにこれを使います。特に、オブジェクトをインタラクティブコンソール内や admin 内で表示する際に該当します。
このメソッドは常に指定することを推奨します; デフォルトはあまり役に立つものではありません。
get_absolute_url()
Django にオブジェクトの URL の計算方法を定義します。Django はこのメソッドを admin インターフェイスで利用しており、オブジェクトの URL を使用する必要があるあらゆる場面でも使用されます。
ユニークに特定できる URL を持つすべてのオブジェクトには、このメソッドを定義しなければなりません。
定義済みのモデルメソッドをオーバーライドする¶
別の一連の モデルメソッド を使って、多くのカスタマイズしたいデータベース動作をカプセル化できます。特に、save()
や delete()
の動作を変更したいことがよくあります。
これらのメソッド (および他のすべてのメソッド) は自由にオーバーライドして、動作を変更できます。
ビルトインのメソッドをオーバーライドする典型的な状況は、オブジェクトを保存するとき毎回何かを実行したい、という場合です。例えば (受け入れるパラメータについては save()
を参照してください):
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()
保存しないようにすることもできます:
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.
スーパークラスのメソッドを呼び出し忘れないようにすることが重要です -- super().save(*args, **kwargs)
のことです -- これでオブジェクトがデータベースに保存されることが保証されます。スーパークラスのメソッドを呼び出し忘れると、デフォルトの動作は発生せずデータベースは変更されません。
モデルのメソッドに引数を渡すこともまた重要です -- *args, **kwargs
が行います。Django は、しばしばビルトインのモデルメソッドの機能を拡張するため、新しい引数を追加します。自分で記述したメソッド定義内に *args, **kwargs
使えば、新しい引数が追加しても自動的にサポートされるようになります。
save()
メソッド内のフィールドの値をアップデートしたい場合は、update_fields
キーワード引数に追加されたこのフィールドを持ちたくなるかもしれません。これにより、update_fields
が指定されたときにフィールドが保存されることが保証されます。次に例を示します。
from django.db import models
from django.utils.text import slugify
class Blog(models.Model):
name = models.CharField(max_length=100)
slug = models.TextField()
def save(
self, force_insert=False, force_update=False, using=None, update_fields=None
):
self.slug = slugify(self.name)
if update_fields is not None and "name" in update_fields:
update_fields = {"slug"}.union(update_fields)
super().save(
force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields,
)
詳しくは どのフィールドを保存するか指定する を読んでください。
オーバーライドされたモデルメソッドはバルク操作では呼ばれません
オブジェクトの delete()
メソッドは、QuerySet を使用したオブジェクトの一括削除 や カスケード削除
を行ったときに、必ずしも呼ばれるわけではないことに注意してください。カスタマイズされた削除ロジックが実行されることを保証するためには、pre_delete
と post_delete
シグナルのいずれかまたは両方が利用できます。
残念ながら、オブジェクトを一括で 作成
または 更新
するときにはワークアラウンドはありません。この場合には save()
、pre_save
、post_save
のいずれも呼ばれないためです。
カスタムの SQL を実行する¶
その他のよくあるパターンとしては、カスタムの SQL 文をモデルのメソッドやモジュールのメソッドとして定義することがあります。生の SQL 文を使用する方法については、生の SQL を使用する を参照してください。
モデルの継承¶
Django におけるモデルの継承は Python における普通のクラスの継承とほぼ同じ方法で行われますが、最初の段階だけでもここで説明しておいた方がよいでしょう。それはその基底クラスが django.db.models.Model
のサブクラスでなければならないという事です。
唯一決めるべきことは、親モデルを (保持するデータベースのテーブルと共に) 独自の権限を持ったモデルとするか、それとも子モデルを通じてのみ共通情報を参照する単なる保持者とするかです。
Django において可能な継承には 3 つの方式があります。
- 子モデルそれぞれに対して一々定義し直さないで済ませたい情報を保持するためだけに、親クラスを使用することがあるかもしれません。このクラスが単体で用いられることはないので、この場合 抽象基底クラス が適切です。
- もし既存のモデル (完全に別のアプリケーション等から取得した) のサブクラスを作成していてモデルそれぞれにデータベース上のテーブルを定義したい場合、 複数テーブルの継承 を利用するとよいでしょう。
- 最後に、モデルの Python レベルでの振る舞いを、モデルのフィールドを変更せずに修正したい場合は、 プロキシモデル を利用できます。
抽象基底クラス¶
抽象基底クラスは、複数の他モデルに対して共通の情報を入れ込みたいときに有用です。基底クラスを書いて Meta クラス内で abstract=True
をセットしてください。これで、このモデルはデータベーステーブルを作成するために使用されることはなくなります。 代わりに、他のモデルで基底クラスとして使われる際に、これら子クラスのフィールドとして追加されます。
実装例:
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
モデルは name
、age
そして home_group
の 3 つのフィールドを持つことになります。 CommonInfo
モデルは抽象基底クラスであるため、通常の Django モデルとしては利用できません。データベース上にテーブルを生成したりマネージャを持ったりせず、そしてインスタンス化されたり直接値を保存する事もできません。
抽象基底クラスを継承したフィールドは、他のフィールドや値でオーバーライドしたり、 None
を使って削除したりできます。
多くの場合、このタイプのモデル継承があなたの必要としているものでしょう。この方式では、データベースレベルでは子モデルごとのテーブルを 1 つずつ作る一方で、共通情報を 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 クラスに対して 1 つの調整を行います: Meta 属性を子クラスに導入する前に、abstract=False
をセットします。これは抽象基底クラスを継承した子クラスは、自身を自動的に抽象基底クラスにはしないことを意味します。他の抽象基底クラスを継承した抽象基底クラスを作るためには、子クラスに abstract=True
を明示的にセットする必要があります。
抽象基底クラスの Meta クラスに持っても意味をなさない属性がいくつか存在します。例えば、属性値 db_table
を指定することは、子クラス (のうち自身の Meta を定義していないもの) が全て同じデータベース上のテーブルを利用することを意味しますが、これは明らかに必要なものではありません。
Python の継承の仕組みにより、子クラスが複数の抽象基底クラスから継承する場合、デフォルトでは、最初にリストされたクラスの Meta オプションのみが継承されます。複数の抽象基底クラスから Meta オプションを継承するには、Meta の継承を明示的に宣言する必要があります。以下に例を示します。
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
ordering = ["name"]
class Unmanaged(models.Model):
class Meta:
abstract = True
managed = False
class Student(CommonInfo, Unmanaged):
home_group = models.CharField(max_length=5)
class Meta(CommonInfo.Meta, Unmanaged.Meta):
pass
複数テーブルの継承¶
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")
もし、Restaurant
でもある Place
が存在する時、小文字にしたモデル名を使うことで、Place
オブジェクトから Restaurant
オブジェクトを取得できます。
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
しかし、上の例において p
が Restaurant
オブジェクト ではない 場合 (つまり、継承を用いず Place
オブジェクトが直接作成されたもしくは他のクラスの親であった場合)、p.restaurant
を参照すると、Restaurant.DoesNotExist
例外が発生します。
Place
にリンクする Restaurant
上に自動的に生成された OneToOneField
は、次のようなものになります。
place_ptr = models.OneToOneField(
Place,
on_delete=models.CASCADE,
parent_link=True,
primary_key=True,
)
Restaurant
上で、自分自身の OneToOneField
を parent_link=True
を付けて宣言すれば、このフィールドをオーバーライドできます。
Meta
と複数テーブルの継承¶
複数テーブルを継承する状況では、子クラスが親クラスの 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')
。
親モデルとのリンクに用いるフィールドを定義する¶
先に述べたように、Django は自動的に OneToOneField
による子クラスから抽象クラスでないあらゆる親モデルへの関連付けを自動的に作ります。もしこの親モデルを指す属性値の名前を設定したい場合は、あるフィールドが親クラスに関連を逆引きするための物であることを示すために、独自の OneToOneField
を作り parent_link=True
を設定できます。
プロキシモデル¶
複数テーブルの継承 を使用すると、モデルの各サブクラスに対して新しいデータベーステーブルが作成されます。これは通常、望ましい動作です。なぜなら、サブクラスには基底クラスに存在しない追加のデータフィールドを格納する場所が必要だからです。しかし、時にはモデルの 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
の属性値を 通常のモデルと同様に 継承します。
QuerySet
s はリクエストされたモデルを返し続ける¶
Person
オブジェクトに対して問い合わせた場合に Django に、あえて言えば、 MyPerson
オブジェクトを返させる事はできません。プロキシオブジェクトのポイントはオリジナルの Person
に依存しているコードはそちらを使いつつ、新たに含んだ (他のコードがどんな形であれ依存しない) 拡張を使うコードを書けるという点です。 Person
モデル (や他の全てのモデル) を新たに作った何物かに置き換える方法ではありません。
基底クラスの制限¶
プロキシモデルは、ちょうど1つの非抽象モデルクラスを継承しなければなりません。プロキシモデルは異なるデータベースのテーブル上の行の間にコネクションを作らないため、複数の非抽象クラスを継承することはできません。プロキシモデルは、いかなるモデルフィールドも定義 しない 限り、抽象モデルクラスをいくつでも継承できます。また、プロキシモデルは、共通の非抽象な親クラスを持つプロキシモデルをいくつでも継承できます。
プロキシモデルマネージャー¶
プロキシモデル上にモデルのマネージャーを定義しない場合、そのプロキシモデルはマネージャーを親のモデルから継承します。プロキシモデル上にマネージャーを定義する場合、親クラス上のマネージャーで定義されて利用可能な物があったとしても、そのマネージャーがデフォルトになります。
先ほどの例の続きで、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 メソッドを隠ぺいする管理対象外モデルを作成できます。しかしながら、この操作は変更を加えた際に定義を複製して同期する際にとても冗長で変更に弱い物となります。
一方、プロキシモデルはプロキシするモデルと全く同じように振る舞うことを企図しています。それらのモデルは直接フィールドとマネージャーを継承しているため、常に親モデルに同期しています。
原則としては:
- もし既存のモデルやデータベースのテーブルを反映したいが元となるテーブルのカラムの全てを必要とする訳ではない場合は、
Meta.managed=False
を利用してください。このオプションはデータベースのビューやテーブルを Django の管理下に置きたくない場合に通常有用となります。 - モデルの Python 上の振る舞いだけを変更したいが、元のモデルと同じフィールドをすべて保持したい場合は、
Meta.proxy=True
を使用します。これにより、データが保存される際にプロキシモデルが元のモデルのストレージ構造の正確なコピーとなるように設定されます。
多重継承¶
Pythonのサブクラス化と同様に、Djangoモデルが複数の親モデルから継承することも可能です。通常の Python 名前解決規則が適用されることを念頭に置いてください。特定の名前 (例えば Meta) が最初に現れる基底クラスが使用されることになります。たとえば、複数の親が 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
あるいは、共通の祖先を使って AutoField
を保持します。このためには、以下のように、自動生成されて子に継承されたフィールド間の衝突を避けるために、各親モデルから共通の祖先への OneToOneField
を明示的に使用する必要があります。
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_name
と related_query_name
と同じように定義します。
これらの特別な属性はその属性を定義しているフィールドが、もはやその特別な属性を定義しなくなるように、変更もしくは削除されなければオーバーライドできません。
親モデルのフィールドをオーバーライドする事はインスタンス (どのフィールドが初期化されるか Model.__init__
に定義している) の初期化やシリアライズ時の問題につながります。これらは通常の Python のクラス継承で全く同様に扱う事が求められていない機能であるため、 Django のモデル継承と Python のクラス継承のこうした差異は恣意的な設計による物ではありません。
この制限は Field
のインスタンスである属性に対してのみ適用されます。通常の Python の属性は望むままにオーバーライドできます。またその制約は Python が解析する際の属性の名称にのみ影響します:もしデータベースのカラム名を手動で設定したい場合、複数テーブルの継承 (2 つの異なるデータベースのテーブルに保持されているカラム) を表現する際に子と祖先のモデルで同じカラム名を設定できます。
もし祖先モデルのいずれかに定義されたモデルのフィールドをオーバーライドした場合 Django は FieldError
例外を送出します。
クラス定義中のフィールドの解決方法のため、複数の抽象親モデルから継承されたモデルのフィールドは、厳密な深さ優先順序で解決されることに注意してください。これは標準の Python の MRO とは対照的です。MRO では、ダイヤモンド型継承の場合には、幅優先で解決されます。この違いの影響を受けるのは複雑なモデル階層のみであり、これは(上記のアドバイスに従って)避けるべきです。
パッケージ化したモデルを扱う¶
manage.py startapp
コマンドを実行することで models.py
ファイルを含むアプリケーション構造が作成されます。多数のモデルが存在する場合、それらを別のファイルとして管理するのが良いかもしれません。
それを行うため、 models
パッケージを作成します。 models.py
を削除してモデルを保持するための __init__.py
ファイルを持つ myapp/models/
ディレクトリを作成します。作成した __init__.py
でモデルをインポートする必要があります。
例えば、 organic.py
および synthetic.py
を models
ディレクトリ内に持っている場合:
from .organic import Person
from .synthetic import Robot
from .models import *
という記述を用いずに各モデルを明示的にインポートする方法には名前空間を汚染しない、可読性を向上させる、コード解析ツールを有用な状態に保つという利点があります。
参考
- モデルの手引き
- モデルのフィールド、関連オブジェクト、
QuerySet
を含む、モデルに関係するすべての API について説明しています。