モデル¶
モデルは、データに関する唯一かつ決定的な情報源です。あなたが保持するデータが必要とするフィールドとその動作を定義します。一般的に、各モデルは単一のデータベースのテーブルに対応付けられます。
基本:
- モデルは各々 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" 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
を忘れずに実行してください。
フィールド¶
モデルにおいて最も重要な箇所であり -- モデルにおいて唯一必須となっている箇所 -- それはそのモデルが定義するデータベースのフィールドの一覧です。フィールドはクラスの属性として定義されます。clean
、save
あるいは 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 は、利用しているフィールドクラスの型に応じて以下のような挙動を決定します。
- データベースに対して保存するデータのタイプを伝えるカラムの型です (例:
INTEGER
、VARCHAR
、TEXT
)。 - フォーム領域をレンダリングする際に利用するデフォルトの HTML ウィジェット (例えば
<input type="text">
、<select>
)。 - 最低限必要とされる入力値確認、Django の管理機能と自動生成されるフォームにおいて利用されます。
Django は多くのフィールド型を内蔵しています; それらの完全な一覧は フィールドの型 で確認できます。もし Django に内蔵されたフィールドの型では実現できない機能を実装したい場合は独自のフィールドを簡単に記述できます; How to create custom model fields を参照してください。
フィールドオプション¶
フィールドはそれぞれあらかじめ定められたフィールド特有の引数 ( モデルのフィールド型一覧 にまとめられています) を受け取ります。例えば、CharField
(およびそのサブクラス) はそのデータを保持するためにデータベース上に定義される VARCHAR
領域の長さを定義する引数 max_length
を必要とします。
全てのフィールドの型で利用できる共通の引数も存在します。すべてオプションの引数です。これらの引数についてはこちらの リファレンス 内で全て説明されていますが、ここでは、特に頻繁に使われるものについて簡単な概要を説明します。
null
True
の場合、Django はデータベース内にNULL
として空の値を保持します。デフォルトはFalse
です。blank
True
の場合、フィールドはブランクになることが許容されます。デフォルトはFalse
です。null
とは挙動が異なる事に注意してください。blank
がバリデーション由来である一方、null
は完全にデータベース由来です。あるフィールドがblank=True
であれば、フォームのバリデーションは空の値の入力を許容します。あるフィールドがblank=False
の場合は、そのフィールドへの入力は必須となります。choices
このフィールドを選択肢として使用するには、2タプルの sequence を使用します。デフォルトのフォームウィジェットが標準のテキストボックスではなくセレクトボックスになり、与えられた選択肢を選ぶように制限されます。
選択肢のリストは以下のようになります。
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.choices, max_length=10)
もっと他の例は model field reference で見ることができます。
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
の場合、そのフィールドはテーブル上で一意となる制約を受けます。
繰り返しになりますが、これらは特によく利用されるフィールドのオプションの概説です。完全な説明は 共通のフィールドオプションの説明 で参照できます。
自動インクリメントのプライマリーキーフィールド¶
By default, Django gives each model an auto-incrementing primary key with the
type specified per app in AppConfig.default_auto_field
or globally in the
DEFAULT_AUTO_FIELD
setting. For example:
id = models.BigAutoField(primary_key=True)
独自の主キーを設定したい場合は、いずれかのフィールドで primary_key=True
を設定してください。そのようにして明示的に Field.primary_key
が設定されている場合、Djangoは自動的に id
カラムを追加しません。
各モデルには、primary_key=True
が必ず 1 つだけ (明示的に宣言されるか、自動的に追加されるかのどちらかで) 存在する必要があります。
In older versions, auto-created primary key fields were always
AutoField
s.
詳細な (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)
# ...
recursive relationships (自分自身に対する多対 1 の関係を持つオブジェクト) や relationships to models not yet defined を作成することもできます; 詳細は the model field reference を参照してください。
必須ではありませんが、ForeignKey
フィールド名 (上記の例では manufacturer
) はモデル名を小文字にしたものをおすすめします。もちろん、どんなフィールドでも呼び出すことができます。例えば、以下のようなフィールド名にすることも可能です。
class Car(models.Model):
company_that_makes_it = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
)
# ...
参考
ForeignKey
フィールドは多数の追加的な引数を受け入れます。これらは the model field reference にて説明しています。これらの引数は、リレーションシップがどのように動作すべきかを定義するのに役立ちます; すべて任意です。
逆向きに関係するオブジェクトへのアクセスの詳細については、下記の 逆向きの関係の例 を参照してください。
サンプルのコードは、Many-to-one relationship model example を参照してください。
多対多 (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
と同様に、recursive relationships (自分自身に対する多対 1 の関係を持つオブジェクト) や relationships to models not yet defined を作成することもできます。
必須ではありませんが、ManyToManyField
フィールド (上記の例では toppings
) は関係モデルオブジェクトの複数形が推奨されています。
どちらのフィールドに ManyToManyField
を定義しても構いませんが、片方のフィールドのみに定義するようにしてください -- 両方ではありません。
一般的に、 ManyToManyField
インスタンスは、フォームで編集されるオブジェクトの側にあります。上の例では、 toppings
は Pizza
の中にあります ( Topping
が pizzas
の ManyToManyField
を持つとするよりはそのほうがいいでしょう ) 。というのは、ピザが複数のトッピングを持つほうが、トッピングが複数のピザの上にあるというよりも自然だからです。このようにすることで、ユーザは `` Pizza`` フォームでトッピングを選ぶことができるようになります。
参考
完全な実装例は Many-to-many relationship model example を参照してください。
ManyToManyField
フィールドは多数の追加的な引数を受け入れます。これらは the model field reference にて説明しています。これらの引数は、リレーションシップがどのように動作すべきかを定義するのに役立ちます; すべて任意です。
多対多リレーションにおける追加フィールド¶
ピザとトッピングを組み合わせる程度の多対多リレーションを扱うのであれば、標準の 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)
中間モデルを指定する場合は、多対多リレーションに参加するモデルに対する外部キーを明示的に指定します。明示的に指定することで、ふたつのモデルがどのように関係するかが定義されます。
中間モデルにはいくつかの制約があります。
- 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 usingManyToManyField.through_fields
. If you have more than one foreign key andthrough_fields
is not specified, a validation error will be raised. A similar restriction applies to the foreign key to the target model (this would bePerson
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.
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>]>
You can also use add()
,
create()
, or
set()
to create
relationships, as long as you specify through_defaults
for any required
fields:
>>> 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)})
You may prefer to create instances of the intermediate model directly.
If the custom through table defined by the intermediate model does not enforce
uniqueness on the (model1, model2)
pair, allowing multiple values, the
remove()
call will
remove all intermediate model instances:
>>> 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>]>
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, 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) 関係¶
1対1のリレーションを定義するには、OneToOneField
を使用します。単純に他の Field
型と同じようにして、モデルのクラス属性に設定することができます。
あるオブジェクトが他のオブジェクトを何らかの方法で「拡張 (extends)」しているとき、オブジェクトの主キーに設定するのが最も便利です。
OneToOneField
には1つの位置引数として、モデルと関係させるクラスを指定する必要があります。
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.
参考
完全な実装例は、1対1リレーションモデルの例 を参照してください。
OneToOneField
フィールドには、オプションの parent_link
引数を指定することもできます。
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.
ファイルを横断したモデル¶
It's perfectly OK to relate a model to one from another app. To do this, import the related model at the top of the file where your model is defined. Then, refer to the other model class wherever needed. For example:
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 クエリに対して、データベースデーブル名とカラム名のエスケープを行っているためです。エスケープ処理では、使用しているデータベースエンジンのクオート構文を利用しています。
カスタムのフィールドタイプ¶
既存のモデルフィールドが目的に適わない場合、あるいは一般的ではないデータベースカラム型を利用したいと考えチエル場合、独自のフィールドクラスを作成することができます。自分自身のフィールドを作成する方法は、How to create custom model fields で提供しています。
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 '%s %s' % (self.first_name, self.last_name)
例にある最後のメソッドは property です。
model instance reference に、各モデルに自動的に与えられるメソッド の完全なリストがあります。 多くなオーバーライド可能です -- 後述の overriding predefined model methods を参照してください -- が、特によく定義することになるものがあります:
__str__()
Python の "魔法のメソッド" で、オブジェクトを表す文字列を返しま。 Python と Django は、モデルのインスタンスを文字列で表示する必要に迫られたときにこれを使います。特に、オブジェクトをインタラクティブコンソール内や admin 内で表示する際に該当します。
このメソッドは常に指定することを推奨します; デフォルトはあまり役に立つものではありません。
get_absolute_url()
Django にオブジェクトの URL の計算方法を定義します。Django はこのメソッドを admin インターフェイスで利用しており、オブジェクトの URL を使用する必要があるあらゆる場面でも使用されます。
ユニークに特定できる URL を持つすべてのオブジェクトには、このメソッドを定義しなければなりません。
定義済みのモデルメソッドをオーバーライドする¶
別の model methods セットを使って、多くのカスタマイズしたいデータベース動作をカプセル化できます。特に、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
使えば、新しい引数が追加しても自動的にサポートされるようになります。
オーバーライドされたモデルメソッドはバルク操作では呼ばれません
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.
カスタムの 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 does make one adjustment to the Meta class of an
abstract base class: before installing the Meta
attribute, it sets abstract=False
. This means that children of abstract
base classes don't automatically become abstract classes themselves. To make
an abstract base class that inherits from another abstract base class, you need
to explicitly set abstract=True
on the child.
抽象基底クラスの Meta クラスに持っても意味をなさない属性がいくつか存在します。例えば、属性値 db_table
を指定することは、子クラス (のうち自身の Meta を定義していないもの) が全て同じデータベース上のテーブルを利用することを意味しますが、これは明らかに必要なものではありません。
Due to the way Python inheritance works, if a child class inherits from multiple abstract base classes, only the Meta options from the first listed class will be inherited by default. To inherit Meta options from multiple abstract base classes, you must explicitly declare the Meta inheritance. For example:
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>
You could also use a proxy model to define a different default ordering on
a model. You might not always want to order the Person
model, but regularly
order by the last_name
attribute when you use the proxy:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
これで通常の Person
に対する問い合わせでは特に結果は並び替えられず OrderedPerson
に対する問い合わせでは last_name
に基づいて並び替えられます。
プロキシモデルは Meta
の属性値を 通常のモデルと同様に 継承します。
QuerySet
s はリクエストされたモデルを返し続ける¶
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 メソッドを隠ぺいする管理対象外モデルを作成できます。しかしながら、この操作は変更を加えた際に定義を複製して同期する際にとても冗長で変更に弱い物となります。
一方、プロキシモデルはプロキシするモデルと全く同じように振る舞うことを企図しています。それらのモデルは直接フィールドとマネージャーを継承しているため、常に親モデルに同期しています。
原則としては:
- もし既存のモデルやデータベースのテーブルを反映したいが元となるテーブルのカラムの全てを必要とする訳ではない場合は、
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
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_name
と related_query_name
と同じように定義します。
これらの特別な属性はその属性を定義しているフィールドが、もはやその特別な属性を定義しなくなるように、変更もしくは削除されなければオーバーライドできません。
親モデルのフィールドをオーバーライドする事はインスタンス (どのフィールドが初期化されるか Model.__init__
に定義している) の初期化やシリアライズ時の問題につながります。これらは通常の Python のクラス継承で全く同様に扱う事が求められていない機能であるため、 Django のモデル継承と Python のクラス継承のこうした差異は恣意的な設計による物ではありません。
この制限は Field
のインスタンスである属性に対してのみ適用されます。通常の Python の属性は望むままにオーバーライドできます。またその制約は Python が解析する際の属性の名称にのみ影響します:もしデータベースのカラム名を手動で設定したい場合、複数テーブルの継承 (2 つの異なるデータベースのテーブルに保持されているカラム) を表現する際に子と祖先のモデルで同じカラム名を設定できます。
もし祖先モデルのいずれかに定義されたモデルのフィールドをオーバーライドした場合 Django は FieldError
例外を送出します。
Note that because of the way fields are resolved during class definition, model fields inherited from multiple abstract parent models are resolved in a strict depth-first order. This contrasts with standard Python MRO, which is resolved breadth-first in cases of diamond shaped inheritance. This difference only affects complex model hierarchies, which (as per the advice above) you should try to avoid.
パッケージ化したモデルを扱う¶
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 について説明しています。