モデル¶
モデルは、データに関する唯一かつ決定的な情報源です。あなたが保持するデータが必要とするフィールドとその動作を定義します。一般的に、各モデルは単一のデータベースのテーブルに対応付けられます。
基本:
モデルは各々 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)
さらに他の例は モデルフィールドのリファレンス で見ることができます。
Changed in Django 5.0:マッピングと callable のサポートが追加されました。
default
そのフィールドのデフォルト値です。このオプションには特定の値または呼び出し可能オブジェクトを渡すことができます。呼び出し可能オブジェクトの場合、新しくオブジェクトが生成される度に呼び出されます。
db_default
フィールドに対するデータベース計算済みのデフォルト値です。リテラル値またはデータベース関数で指定できます。
もし
db_default
とField.default
の両方が指定された場合、Python コードでインスタンスを作成する際にはdefault
が優先されます。データベースレベルでもdb_default
が設定され、ORM の外部で行を挿入するときや、マイグレーションで新しいフィールドを追加するときに使用されます。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
を除くすべてのフィールド型は、任意の第 1 引数として "詳細な名前 (verbose name)" を取ります。詳細な名前を指定しない場合、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
には位置引数が1つ必要で、ここにはモデルと関係付けるクラスを指定します。
たとえば、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 がモデルインスタンスを文字列として表示する際に、このメソッドが使用されます。最も典型的な例は、インタラクティブなコンソールや管理画面でオブジェクトを表示するときです。
このメソッドは必ず定義することを推奨します。デフォルト値はあまり役に立ちません。
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, **kwargs):
do_something()
super().save(**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, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super().save(**kwargs) # Call the "real" save() method.
親クラスのメソッド、つまり super().save(**kwargs)
を忘れずに呼び出すようにします。これを呼び出さないと、オブジェクトがデータベースに保存されなくなります。親クラスのメソッドを呼び出さない場合、デフォルトの動作が行われず、データベースに変更が反映されません。
また、モデルメソッドに渡される可能性のある引数をそのまま渡すことも重要です。これが **kwargs
の役割です。Djangoは時折、組み込みのモデルメソッドの機能を拡張し、新しいキーワード引数を追加します。メソッド定義で **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, **kwargs):
self.slug = slugify(self.name)
if (
update_fields := kwargs.get("update_fields")
) is not None and "name" in update_fields:
kwargs["update_fields"] = {"slug"}.union(update_fields)
super().save(**kwargs)
詳しくは どのフィールドを保存するか指定する を読んでください。
オーバーライドされたモデルメソッドは一括操作では呼び出されません
オブジェクトの 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 属性に 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
例外が発生します。
Restaurant
上には次のように、 Place
と関連付けられた 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 は、非抽象の親モデルにリンクする :class:~django.db.models.OneToOneField を子クラスに自動的に作成します。親モデルへのリンクとなる属性の名前を自分で決めたい場合は、 :class:~django.db.models.OneToOneField を手動で作成し、そのフィールドが親クラスへのリンクであることを示すために :attr:parent_link=True <django.db.models.OneToOneField.parent_link> を設定します。
プロキシモデル¶
マルチテーブル継承 を使用すると、モデルの各サブクラスに対して新しいデータベーステーブルが作成されます。これは通常、望ましい動作です。なぜなら、サブクラスには基底クラスに存在しない追加のデータフィールドを格納する場所が必要だからです。しかし、時にはモデルの 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 はリクエストされたモデルを返し続ける¶
Django では、例えば Person
オブジェクトにクエリした際に、MyPerson
オブジェクトを返すようにする方法はありません。Person
オブジェクトのクエリセットは、そのまま Person
オブジェクトを返します。プロキシオブジェクトのポイントは、Person
モデルに依存するコードはそのまま 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 クラスが含まれている場合、最初のものだけが使用され、それ以降のものは無視されます。
通常、複数の親クラスを継承する必要はありません。これが有用となる主なケースは、「ミックスイン (mixin)」クラスを使う場合です。ミックスインを使うと、すべての子クラスで特定の追加フィールドやメソッドを利用できます。ただし、継承階層はできるだけシンプルで分かりやすく保つようにしてください。そうすることで、特定の情報がどこから来ているのかを把握するために苦労せずに済みます。
共通の 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 について説明しています。