revision-up-to: | 17812 (1.4) |
---|
モデルとは、サイトを構成するデータの、唯一絶対的なデータソースを指します。 モデルには、保存したいデータを表すデータフィールドと、データのビヘイビアを 定義します。通常、一つのモデルは一つのデータベーステーブルに対応しています。
モデルの基本として、以下のことを知っておいてください:
django.db.models.Model
のサブ
クラスです。以下の例では、 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
はモデルの フィールド (fields) です。各
フィールドはクラスのアトリビュートとして定義します。各々のアトリビュートは
データベースのカラムに対応します。
上の 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
はモデルのメタデータから自動的に決定
します。テーブル名は変更できます。「 テーブル名 」を参照して
ください。id
は自動的に追加されます。この動作も変更できます。
「 主キーフィールドの自動設定 」を参照してください。CREATE TABLE
SQL 文は PostgreSQL の書式で書かれています
が、 Django は 設定ファイル に指定しておいた
データベースバックエンドに合わせて適切な SQL を発行します。モデルを定義したら、そのモデルを 使う よう Django に指示しましょう。設定
ファイルを編集して、 models.py
の入っているパッケージの名前を
INSTALLED_APPS
設定に追加してください。
例えば、アプリケーションのモデルが mysite.myapp.models
モジュール
(mysite.myapp
は manage.py startapp
スクリプトで
作成したアプリケーションのパッケージ) に入っているなら、
INSTALLED_APPS
に以下のように書きます:
INSTALLED_APPS = (
#...
'mysite.myapp',
#...
)
新しいアプリケーションを INSTALLED_APPS
に追加したら、
manage.py syncdb
を忘れずに実行してください。
モデルのフィールドは、モデルの定義で最も重要な、かつ最小限必要な要素です。 フィールドは、以下の例のように、クラスのアトリビュートとして定義します:
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)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
モデルの各フィールドには、 Field
クラスのサブク
ラスから、適切なクラスのインスタンスを生成して入れます。Django は、フィール
ドのクラスが何であるかという情報を、以下のような判定で使います:
INTEGER
, VARCHAR
など) の決定<input type="text">
,
<select>
など) の決定Django にはたくさんのフィールドクラスが組み込みで付属しています。組み込みの フィールドクラス一覧は「 モデルフィールドリファレンス 」にあります。組み込みのフィールドで実現できないことが あれば、フィールドクラスを自分で定義できます。 「 howto-custom-model-fields 」を参照してください。
各フィールドクラスには、それぞれフィールド固有の引数がいくつかあります (詳
しくは「 モデルフィールドリファレンス 」を参照し
てください)。例えば、 CharField
(とそのサブクラ
ス) には、データを保存するときの VARCHAR
データベースフィールドの長さを
決める必須の引数、 max_length
が必要で
す。
また、全てのフィールドに共通の引数もあります。共通の引数はどれも省略可能で す。共通の引数は「 リファレンス 」で説 明しています。
ここでは、良く使う引数を簡単に紹介しましょう:
null
True
にすると、フィールドの値が空のとき、データベースには
NULL
を保存します。デフォルトの設定は False
です。blank
True
にすると、フィールドの値を空白 (blank) にできます。デフォ
ルトの設定は False
です。
null
とは違うので注意してください。
null
は、単に空のデータをデータベース上でどう表現す
るかを決めています。一方、 blank
はフィールドの値の
検証 (validation) 方法を決めています。あるフィールドに
blank=True
を指定すると、 Django の管理サイト
上で、そのフィールドを空の値にしたままエントリを作れます。
blank=False
にすると、フィールドには必ず値を
入れねばなりません。
choices
iterable (リストまたはタプル) を指定します。 iterable の各要素は
フィールドの値の選択肢を表す 2 要素のタプルです。
choices
を指定すると、 Django の管理サイト上には、標
準的なテキストフィールドの代わりにセレクタボックスが表示され、ユー
ザは選択肢の値だけを選べます。
選択肢のタプル列は、例えば以下のように定義します:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
各タプルの最初の要素が実際にデータベースに保存される値で、二つめの
要素が管理インタフェースや ModelChoiceField
に表示される内容で
す。あるモデルオブジェクトのインスタンスで後者の値 (表示値) を取り
出したければ、 get_FOO_display
メソッドを使います。例えば、以下
のようなモデルを考えましょう:
from django.db import models
class Person(models.Model):
GENDER_CHOICES = (
(u'M', u'Male'),
(u'F', u'Female'),
)
name = models.CharField(max_length=60)
gender = models.CharField(max_length=2, choices=GENDER_CHOICES)
gender
の表示値は get_gender_display
で取り出します:
>>> p = Person(name="Fred Flinstone", gender="M")
>>> p.save()
>>> p.gender
u'M'
>>> p.get_gender_display()
u'Male'
default
help_text
primary_key
True
を指定すると、フィールドをモデルの主キーにします。
モデルのどのフィールドにも primary_key=True
が指定されていない場合、Django は自動的に
IntegerField
を追加して主キーを保存します。従って、主キー
の設定をデフォルトの状態から変更したいのでないかぎり、
primary_key=True
を指定する必要はありま
せん。詳しくは「 主キーフィールドの自動設定 」を参照してく
ださい。
unique
True
の場合、フィールドの値はテーブル全体で一意でなければなりま
せん。前置きしたように、ここではよく使うオプションを簡単に説明するにとどめます。 全オプションの詳しい情報は 「 共通のモデルフィールドオプションのリファ レンス 」を参照してください。
デフォルトの動作では、 Django は各モデルに以下のフィールド:
id = models.AutoField(primary_key=True)
を追加します。このフィールドは主キーに使われ、フィールドの値はレコードの追 加ごとに自動的にインクリメントされてゆきます。
特定のフィールドを主キーにしたければ、フィールドのオプションに
primary_key=True
を指定してください。
Field.primary_key
の設定されたフィールドの定義があれば、Django は
id
カラムを自動的に追加しません。
各モデルには必ず一つ primary_key=True
のフィー
ルドが必要です。
ForeignKey
と
ManyToManyField
と
OneToOneField
を除くフィールドクラスは、
第一引数に表示用のフィールド名を指定できます。この引数は省略可能です。引数
を指定しない場合、 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
クラスでは、第一引数は必須
で、リレーション先のモデルのクラス名を指定せねばなりません。これらのフィー
ルドクラスでは、 verbose_name
キーワード引数を使って表示用の
フィールド名を指定します:
poll = models.ForeignKey(Poll, verbose_name="the related poll")
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(Place, verbose_name="related place")
慣習的に、 verbose_name
の先頭の文字は大文字にしません。
Django は必要なときに先頭の文字を自動的に大文字にします。
リレーショナルデータベースの威力が、テーブルを相互に関連づけする機能である のはいうまでもありません。Django には、多対一 (many-to-one)、多対多 (many-to-many)、一対一 (one-to-one) といった、よく使うリレーションを定義す る機能があります。
多対一のリレーションを定義するには
ForeignKey
を使います。このフィールドは他
のフィールド型と同じように、モデルのクラスアトリビュートとして定義できます。
ForeignKey
は、リレーションを張る対象のク
ラスを指定する必須の引数を一つとります。
例えば、 Car
モデルに Manufacturer
というフィールドを持たせたいとし
ましょう。すなわち、ある Manufacturer
には複数の Car
が対応するが、
各 Car
には一つだけ Manufacturer
が対応するようにしたいとしましょう。
この場合、以下のように定義します:
class Manufacturer(models.Model):
# ...
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
# ...
再帰的リレーション (自分自身に対する多対多 のリレーション) や、 未定義のモデルに対するリレーション も定義できます。「 モデルフィールドのリファレン ス 」を参照してください。
必須ではありませんが、 ForeignKey
の名前
(上の例では manufacturer
) には、モデル名を小文字にしたものを使うよう勧
めます。もちろん、以下のように、好きな名前を付けてもかまいません:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(Manufacturer)
# ...
See also
ForeignKey
フィールドは
モデルフィールドリファレンス で説明してい
る多くの追加引数を受け取ります。これらのオプションはリレーションがどのよ
うに動くべきかを定義するのに役立ちます。
逆参照されるオブジェクトへのアクセスについては リレーションの逆参照の例. に詳しく書いてあります。
多対多の (many-to-many) リレーションを定義するには
ManyToManyField
を使います。このフィール
ドは他のフィールド型と同じように、モデルのクラス属性に含めて使えます。
ManyToManyField
には固定引数が一つあり、
リレーションを張る対象のクラスを指定します。
Pizza
には複数の Topping
オブジェクトを関連付ける例を考えてみましょ
う。すなわち、ある Topping
は複数のピザの上に置けて、逆にそれぞれのピザ
には複数のトッピングを置けるというわけです。このリレーションを表現するには
以下のように定義します:
class Topping(models.Model):
# ...
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
ForeignKey
と同様、 再帰的リレーション (自分自身に対する多対多のリレーション) や、
未定義のモデルに対するリレーション も定義できま
す。「 モデルフィールドのリファレンス 」を参照して
ください。
必須ではありませんが、 ManyToManyField
の
名前 (上の例では toppings
)は、リレーション先のモデル名の複数形にするよ
う勧めます。
どちらのモデルで ManyToManyField
を定義し
てもかまいませんが、どちらか片方のモデルだけ指定すべきです – 両方には指定
すべきではありません。
一般的に言って、 Django の管理インタフェースを使うのなら、
ManyToManyField
インスタンスを入れておく
のは、管理インタフェースで編集される側のオブジェクトにしておくとよいでしょ
う。上の例では、 topping
は (Topping
に
ManyToManyField
の pizzas
をもたせる
のではなく) Pizza
に入れておきます。というのも、「トッピングを乗せるピ
ザ」を編集するよりも「ピザの上に乗せるトッピング」を編集する方が自然だから
です。というわけで、上の例のようにすれば、管理サイトの Pizza
の編集画面
上でユーザにトッピングを選ばせられます。
See also
ManyToManyField
フィールドも
モデルフィールドリファレンス で説明して
いる多くの引数を受け取ります。これらのオプションはリレーションがどの
ように動くべきかを定義するのに役立ちます。必須ではありません。
ピザとトッピングを組み合わせたり照合したりするだけのような多対多のリレーショ
ンを扱いたいのなら、標準の ManyToManyField
で事
足ります。しかし、時として、二つのモデルのリレーションそのものにデータを関
連づけたい場合があります。
例えば、ミュージシャンのグループと、在籍しているミュージシャンを追跡するア
プリケーションのケースを考えてみましょう。これは個人 (person) とグループの
関係なので、 ManyToManyField
でリレーションを表
現できるはずです。しかし、ミュージシャンとグループの関係には、あるミュージ
シャンがあるグループにいつ合流したか、のような細かい情報がたくさんあります。
こうした状況に対応するために、Django には、あらかじめリレーションを管理する
ためのモデル (中間モデル) を定義しておき、その中間モデルに向けてエクストラ
フィールド (extra field) を組み込んで多対多のリレーションを定義する方法があ
ります。中間モデルは ManyToManyField
の
through
引数に指定します。ミュージシャン
の例は以下のように書けます:
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
中間モデルからは、多対多のリレーションの両側にあたるモデルに対して明示的に 外部キーを張らねばなりません。
中間モデルには、いくつか制約があります:
Person
ですね)。同じモデルに複数の外部キーを定義する
と、モデルのバリデーション時にエラーを送出します。Group
ですね)。同じモデルに複数の外部キーを定義すると、モデル
のバリデーション時にエラーを送出します。symmetrical=False
を指定せねばなりません (「 モデルフィールドのリファレンス 」を参照してください) 。さて、これで中間モデル (例でいうところの Membership
) を使った
ManyToManyField
を設定したので、多対多のリレーショ
ンを生成できます。リレーションの生成は、中間モデルのインスタンス生成によっ
て実現します:
>>> 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()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<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()
[<Person: Ringo Starr>, <Person: Paul McCartney>]
通常の ManyToManyField
と違って、 add
や create
,
(beatles.members = [...]
のような) 代入では、リレーションの定義は行えま
せん:
# 使えません。
>>> beatles.members.add(john)
# これも使えません。
>>> beatles.members.create(name="George Harrison")
# これもまた使えません。
>>> beatles.members = [john, paul, ringo, george]
なぜでしょうか。中間モデルでリレーションを作成するときには、単に Person
と Group
の間のリレーションだけではなく、 Membership
テーブルのレコー
ドを生成するための情報が全て必要だからです。単に add
や create
を呼
び出したり代入を行うだけでは、リレーション以外の情報を埋められません。その
結果、中間モデルを使う場合は、 add
や create
は使えないのです。
中間モデルを使っている場合、多対多のリレーションを保存するには、中間モデル
のインスタンスを生成するしかない、ということを覚えておいてください。
同じ理由で remove()
メソッドも使えません。 ただし、
clear()
メソッド
を使えば、以下のように多対多のリレーションを一括して消去できます:
# ビートルズ解散
>>> beatles.members.clear()
ひとたび中間モデルによる多対多のリレーションを作成したら、通常の多対多リレー ションと同じように、リレーション先のモデルのアトリビュートを使ってリレーショ ンをまたいだクエリを実行できます:
# 'Paul' で始まる名前の人がメンバにいるグループを探す
>>> Groups.objects.filter(members__name__startswith='Paul')
[<Group: The Beatles>]
中間テーブルを使っているので、中間テーブルのアトリビュートを使ったクエリも 実行できます:
# 1961年1月1日以降に合流した Beatles のメンバを探す
>>> Person.objects.filter(
... group__name='The Beatles',
... membership__date_joined__gt=date(1961,1,1))
[<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
u'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
u'Needed a new drummer.'
一対一のリレーションを定義するには、
OneToOneField
を使います。このフィールド
も他のフィールド型と同じように、モデルクラスのアトリビュートとして定義しま
す。
このリレーションがもっとも有用なのは、あるオブジェクトが別のオブジェクトを 何らかの形で「拡張」している場合の主キーとして使う場合です。
OneToOneField
には固定引数が一つあり、リ
レーションを張る対象のクラスを指定します。
例えば、「場所 (place)」のデータベースを作るときには、アドレス、電話番号、
といった標準的なものを作成します。その上で、レストランデータベースを作成
するときに、 Restaurant
モデルの中に同じフィールドをつくるような
繰り返し作業 (repeat yourself) をする代わりに、場所データベースを基盤にして
レストランのデータベースを作成したいとしましょう。このとき、 Restaurant
には Place
への OneToOneField
をもた
せられます (レストランは場所に対して “is-a” の関係だからです。実際には、こ
のような状況を扱うのに モデルの継承 を使いますが、
モデル継承は OneToOneField
で実現されてい
ます)。
ForeignKey
と同様、
再帰的リレーション や
未定義のモデルに対するリレーション も定義できま
す。詳しくは「 モデルフィールドのリファレンス 」を参
照してください。
See also
OneToOneField
フィールドは、オプションの引数を一
つとります。引数は「 モデルフィールドリファレンス 」
で解説しています。
以前は、モデル中で OneToOneField
を使うと、
そのフィールドは自動的にモデルの主キーになっていました。この仕様はもうなく
なりましたが、主キーにしたければ手動で primary_key
に指定できます。この
変更により、一つのモデル中で複数の
OneToOneField
を使えるようになりました。
あるモジュールから別のモジュールへのリレーションは全く問題なく張れます。リ レーションを張るには、以下の例のようにモデル定義の先頭でリレーション対称の モデルを import しておき、必要なところでそのモデルクラスを参照します:
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(ZipCode)
Django がモデルのフィールド名に課している制約は二つしかありません:
フィールド名は Python の予約語であってはなりません。さもないと Python のシンタクスエラーを引き起こすからです。
悪い例:
class Example(models.Model):
pass = models.IntegerField() # 'pass' は予約語です!
フィールド名には二つ以上の連続するアンダースコアを入れてはなりません。 なぜなら、Django は二重アンダースコアをクエリ照合構文で使っているか らです。
悪い例:
class Example(models.Model):
foo__bar = models.IntegerField() # 二重アンダースコアがあります!
データベースのカラム名はフィールドの名前と一致していなくてもよいので、デー
タベース側では制約を回避できます。詳しくは後述の db_column
を
参照してください。
join
や where
, select
のような SQL の予約語を、モデルのフィール
ド名に使っても かまいません 。というのも、Django は SQL クエリを生成する
ときにデータベーステーブル名やカラム名を常にエスケープするからです。エスケー
プにはデータベースエンジン固有のクオート方式を使います。
既存のモデルフィールドが目的とするアプリケーションに合わない場合や、あまり 一般的でないデータベースカラムタイプを活用したい場合のために、独自のフィー ルドクラスを作成できます。カスタムフィールドの詳しい作成方法は 「 howto-custom-model-fields 」で説明しています。
モデルにメタデータを指定するには、以下のようにモデルの内部クラス Meta
を使います:
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
に指定できるオプションの一覧は、「 モデルオプションのリファ
レンス 」で解説しています。
カスタムの行レベル (“row-level”) の機能をオブジェクトに実装するには、カスタ
ムのメソッドを定義します。 Manager
メソッドの目
的が「テーブル級 (table-wide)」 の操作であるのに対し、モデルメソッドは個々
のモデルインスタンス単位の操作を実現します。
モデルメソッドは、ビジネスロジックの置き場所をモデル一箇所に限定するための 効果的なテクニックです。
例えば、以下のモデルでは、いくつかカスタムメソッドを定義しています:
from django.contrib.localflavor.us.models import USStateField
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
address = models.CharField(max_length=100)
city = models.CharField(max_length=50)
state = USStateField() # Yes, this is America-centric...
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31):
return "Baby boomer"
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
return "Post-boomer"
def is_midwestern(self):
"Returns True if this person is from the Midwest."
return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
def _get_full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
この例の最後のメソッドは プロパティ (property) です。
「 モデルインスタンスのリファレンス 」では、 各モデルに自動的に付与されるメソッド を全て 紹介しています。ほとんどのメソッドはオーバライド可能です。詳しくは後述の 既存のモデルメソッドをオーバライドする を参照してください。といっても、 たいていはせいぜい以下の 2 つのメソッドをオーバライドするぐらいで済むでしょ う:
__unicode__()
オブジェクトの「Unicode 表現」を返す、Python の「マジックメソッド」 です。 Python や Django はこのメソッドを使ってモデルインスタンスに 型強制を適用し、通常の文字列に変換したり表示したりしています。特に、 対話コンソールや管理インタフェース上でオブジェクトを表示するときに 使われています。
このメソッドは常に定義するよう勧めます。定義しておくと、いろいろと 便利だからです。
get_absolute_url()
このメソッドを定義しておくと、 Django にオブジェクトの URL を計算さ せられます。 Django の管理インタフェースなどで、オブジェクトに関連 した URL を特定するときにこのメソッドを使います。
オブジェクトを一意に特定できるような URL を持たせたいときには、この メソッドを定義しましょう。
他にも、データベースの動作をカプセル化していて、カスタマイズ可能な モ
デルメソッド があります。特によく変更するのは、
save()
や delete()
の動作でしょう。
こうしたモデルメソッドは、モデルオブジェクトの挙動を変えるために好きなだけ オーバライドできます。
組み込みメソッドをオーバライドする古典的なユースケースに、オブジェクトの保
存時に何か別の処理を行うというものがあります。例を示しましょう
(save()
のパラメタはドキュメントを参照してください。):
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super(Blog, self).save(*args, **kwargs) # 「実際の」 save() を呼び出します。
do_something_else()
また、以下のようにすれば保存を抑止できます:
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 は自分のブログを持っていません!
else:
super(Blog, self).save(*args, **kwargs) # 「実際の」 save() を呼び出します。
スーパクラスのメソッド呼び出し、つまり
super(Blog, self).save(*args, **kwargs)
を忘れないでください。この呼び
出しを忘れると、デフォルトの処理が行われないため、データベースにデータが保
存されません。
*args, **kwargs
によってモデルメソッドに渡せる引数をパススルーすること
も重要です。 Django は時々ビルトインのモデルメソッドに新しい引数を追加して
拡張することがあります。メソッド定義で *args, **kwargs
を使っておけば、
これらの引数が追加されても自動でサポートされることが保証されます。
削除のオーバーライド
QuerySet による一括オブジェクト削除
の時にはオブジェクトの delete()
メソッドが呼ばれないこ
とに注意してください。カスタマイズした削除ロジックが実行されることを
保証するには
pre_delete
と
post_delete
シグナルのいずれかまたは
両方を使えば良いでしょう。
もう一つよくあるパターンは、カスタムの SQL 文をモデルメソッドやモジュールレ ベルのメソッドにに書くというものです。詳しくは、 「 素の SQL を扱う 」を参照してください。
Django のモデルクラス継承は、 Python の通常のクラス継承とほぼ同じように動作 します。モデル継承を実現するには、一つだけきめておかねばならないことがあり ます。それは、親クラスを (独自のデータベーステーブルを持った) 独立のモデル にしたいか、親クラスを共通情報の単なるホルダとして使い、子クラスでその情報 を扱う形にしたいかです。
Django には 3 種類のモデル継承スタイルがあります。
抽象ベースクラスは、たくさんのモデルに共通する情報を入れておきたいときに便
利な仕組みです。抽象ベースクラスを定義するには、ベースクラス定義の、
Meta に abstract=True
を入れておきます。モデルを
抽象ベースクラスとして定義すると、そのクラスはデータベーステーブルを生成し
ません。その代り、他のモデルを定義するときに抽象ベースクラスを親にすると、
ベースクラスのフィールドが子クラスに追加されます。抽象ベースクラスで定義し
たフィールドと同じ名前のフィールドを子クラスで定義しようとするとエラーを引
き起こします (Django は例外を送出します)。
例を示しましょう:
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
の三つのフィールドを持つようになります。. CommonInfo
モ
デルは抽象ベースクラスなので、通常の Django モデルとしては使えません。
CommonInfo
はデータベーステーブルを作らず、マネジャなども持ちません。
ほとんどの用途で使えるのは、このタイプのモデル継承でしょう。抽象ベースクラ スは、子クラスで共通の情報を Python レベルに切り出しながらも、データベース レベルでは、各子クラスに一つのデータベーステーブルを生成します。
Meta
の継承¶抽象ベースクラスを作成するとき、 Django は抽象ベースクラスの内部クラス Meta をクラス属性として参照できるようにします。子クラ スで Meta クラスを定義しない場合、親クラスの Meta がそのまま継承されます。子クラスで Meta を拡張したければ、親クラスをサブクラス化できます。 以下に例を示しましょう:
class CommonInfo(models.Model):
...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
Django は抽象ベースクラスの Meta クラスを生成するとき
に一つだけ手を加えます。すなわち、 Meta の属性を組み
込む前に、 abstract=False
を設定するのです。これにより、抽象ベースクラ
スの子クラスが自動的に非抽象ベースクラスになります。もちろん、他の抽象ベー
スクラスを継承した新たな抽象ベースクラスも定義できます。継承するには、明示
的に abstract=True
をセットしてください。
Meta クラスの属性の中には、抽象ベースクラスで定義して
も意味のないものもあります。例えば、 db_table
を抽象ベースクラスの
Meta で定義すると、その子クラス全て (で、独自に
Meta を定義しないもの) が、同じデータベーステーブルを
使おうとしてしまいます。
Django では、継承の各階層にいるクラスが抽象クラスでない実際のモデルであるよ
うな、もう一つのタイプのモデル継承をサポートしています。各モデルはそれぞれ
が一個のデータベーステーブルを表現していて、個別にクエリを発行したり、イン
スタンスを生成したりできます。継承の関係によって、親クラスと子クラスの間に
は (自動生成された OneToOneField
によって) リン
クが張られます。以下の例で説明しましょう:
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
Place
の全てのフィールドは、 Restaurant
からも使えます。しかし、そ
れぞれのフィールドのデータは別々のテーブルに格納されます。従って、以下のよ
うな操作をどちらも実行できます:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
例えば、 Place
であり、かつ Restaurant
でもあるようなオブジェクトが
あれば、モデル名を小文字にした属性を使って、 Place
から Restaurant
を取り出せます:
>>> p = Place.objects.get(id=12)
# p が Restaurant オブジェクトなら、子クラスを返す:
>>> p.restaurant
<Restaurant: ...>
ただし、上の p
が Restaurant
クラスで ない のなら (Place
から
直接生成されたオブジェクトや、他のクラスの親クラスなら) p.restaurant
はエラーを引き起こします。
Meta
とマルチテーブル継承¶マルチテーブル継承では、子クラスが親クラスの Meta ク ラスを継承する意味がありません。 Meta オプションは全 て親クラスだけに適用されるべきもので、他のクラスに同じ内容を適用しても、矛 盾した振る舞いを引き起こすだけです(これは、独自のテーブルを持たない抽象ベー スクラスと対照的な部分です)。
従って、マルチテーブル継承の子クラスは親クラスの Meta
クラスにアクセスする方法を持ちません。ただし、限られたケースで、子クラスが
親クラスの挙動を継承する場合があります。それは、子クラスで
ordering
または
get_latest_by
属性を指定していない場合に、
親クラスの属性を継承するというものです。
親クラスで整列カラムが指定されていて、子クラス側で整列カラムを指定したくな い場合は、明示的設定を無効にしてください:
class ChildModel(ParentModel):
...
class Meta:
# 親クラスの整列カラム設定の効果を除去する
ordering = []
マルチテーブル継承は親クラスと子クラスを暗黙の
OneToOneField
でリンクするので、前述の例のように、
親クラスから子クラスをたどれます。しかし、子クラスをたどるときに使う名前は、
ForeignKey
や
ManyToManyField
リレーションを定義したと
きのデフォルトの related_name
と同じ値
を使っています。従って、子クラスで
ForeignKey
や
ManyToManyField
を別のクラスに向けて張る
ときには、 必ず related_name
をフ
ィールドに指定せねばなりません。指定し忘れると、 Django は
validate
や syncdb
時にエラーを送出します。
例えば、上の Place
クラスに対して、
ManyToManyField
を持った別のサブクラスを定義する
には、以下のように書きます:
class Supplier(Place):
# Must specify related_name on all relations.
customers = models.ManyToManyField(Restaurant, related_name='provider')
すでに触れたように、 Django は子クラスと抽象クラスでない親クラスにリンクし
た OneToOneField
を自動的に生成します。親クラス
へのリンク名を操作したければ、自分で
OneToOneField
を作成し、パラメタに
parent_link=True
を渡
してください。
マルチテーブル継承 を使う時には、各々のモ デルのサブクラスに新しくデータベーステーブルが作られます。サブクラスはデー タフィールドを追加して、親クラスにはない新しいデータを保存する箇所を増やす ので、これは一般的には望まれる動作です。
しかしながら、デフォルトのマネジャーを変えたり新しいメソッドを追加すること によって、同じモデルに対して Python の動作を変えたいだけのときがあります。 これが、プロキシモデル継承がある理由です。オリジナルのモデルに対して プロキシ を作ります。そうすれば、ただオリジナルのモデルを使用しているよ うに、プロキシモデルのインスタンスを作成、更新、削除して、全てのデータをセ ーブすることが出来ます。違いは、モデルの順序づけやデフォルトのマネジャーを オリジナルを改変せずに変更できるということです。
プロキシモデルは通常のモデルと同じように宣言します。 Django に対してそのモ
デルがプロキシモデルであることを教えるためには、 Meta
クラス内の
proxy
アトリビュートを True
にします。
例えば、標準の User
モデルに対してテ
ンプレートの中で用いるメソッドを追加したい場合、このように定義します:
from django.contrib.auth.models import User
class MyUser(User):
class Meta:
proxy = True
def do_something(self):
...
MyUser
クラスは親である User
クラ
スと同じデータベーステーブル上で動作します。特に、
User
のどんな新しいインスタンスでも、
MyUser
を通してアクセスすることができます。逆も可能です:
>>> u = User.objects.create(username="foobar")
>>> MyUser.objects.get(username="foobar")
<MyUser: foobar>
また、モデルに別のデフォルトオーダーを定義する場合にもプロキシモデルを用い
ることができます。標準の User
モデル
ではオーダーが定義されていません (ソートはコストがかかるので、ユーザを取得
する時にいつもそうしたくないため、故意にそうしています。) プロキシを使って
いて、いつも username
属性で順序づけたい場合は、こうすると簡単です:
class OrderedUser(User):
class Meta:
ordering = ["username"]
proxy = True
こうすると、普通の User
のクエリでは
順序が定められていませんが OrderedUser
のクエリは username
で順序
づけられます。
User
オブジェクトへのクエリで Django
に MyUser
オブジェクトを返させることはできません。 User
オブジェク
トへのクエリセットは User
型のオブジェクトを返すでしょう。プロキシオブ
ジェクトの全体的なポイントは、元の User
に依存しているコードは元のモデ
ルを使い、追加したコードは拡張を使えるということです。(他のどんなコードも
追加のコードに依存しません。) プロキシモデルは User
(または他のどんな
モデルも) を自分で作成したもので置き換える手段ではないのです。
プロキシモデルは抽象クラスでないモデルクラスを 1 つだけ継承しなくてはなり ません。抽象クラスでないモデルクラスの多重継承をすることはできません。プロ キシモデルは、異なるデータベーステーブルの行へのコネクションを提供していな いからです。プロキシモデルはモデルフィールドを定義して いない 抽象モデル クラスならばいくつでも継承することができます。
プロキシモデルは親の抽象モデルクラスから Meta
オプションを継承します。
もしプロキシモデルにモデルマネジャーを指定しなかった場合、マネジャーはモデ ルの親から継承します。もしマネジャーを定義すれば、それがデフォルトになりま す。それでも、親クラスに指定されたマネジャーを使用することも出来ます。
上述した例を続けましょう。User
モデルに問い合わせる時使われるデフォル
トのマネジャーを、このように変更することが出来ます:
class NewManager(models.Manager):
...
class MyUser(User):
objects = NewManager()
class Meta:
proxy = True
もしプロキシに新しいマネジャーを加えたい場合、デフォルトで存在するものと置 き換えることなく、付け加えることができます。 custom manager のドキュメントで述 べているテクニックを使うことができます。 新しいマネジャーを含んだベースクラスを作成して、主なベースクラスの後にそれ を継承しています:
# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
secondary = NewManager()
class Meta:
abstract = True
class MyUser(User, ExtraManagers):
class Meta:
proxy = True
このテクニックを必要とすることはそんなにないでしょうが、可能です。
プロキシモデルの継承は、マネージされないモデルを作るのによく似ています。
マネージされないモデルは、モデルの Meta
クラスに
managed
属性を使うものです。
2 つの方法は全く同じものではないので、どちらを使った方がよいのか熟慮する
価値があります。
一つ目の違いは Meta.managed=False
のモデルにはモデルフィールドを指定
できることです。 (そして実際、空のモデルを欲しいのでなければ、そうしなく
てはいけませんね) 注意深く
Meta.db_table
を注意深く設定
することで、既存のモデルの影のような、しかし Python メソッドを追加する
マネージされないモデルを作れます。しかしながら、それは新しく変更を加え
る時に両方のコピーを同期させ続けなければならない脆弱なものになります。
もう 1 つの違いは、どのようにモデルのマネジャーが扱われるのかという、プ ロキシモデルにとってもっと大切な点です。プロキシモデルが意図しているの は、代理しているモデル自体のようにふるまうことです。だからデフォルトの マネジャーを含む親のモデルマネージャーを継承します。多テーブルのモデル を継承する場合、子は親からマネジャーを継承しません。エクストラフィールド が関係している場合は、カスタムマネジャーが適切であるとは限らないからです。 マネジャードキュメント にもっ と詳細な内容が載っています。
これら 2 つの特徴が実装されている時、これらを 1 つのオプションに押し込 めることが試みられました。そして継承の相互作用は一般的に、特にマネジャー は、 API をとても複雑にし、理解して使うことを難しくする可能性があると分 かりました。 2 つのオプションはあるケースでは必要だと分かりました。それ で、現在は 2 つのオプションが存在しています。
一般的なルールとして:
Meta.managed=False
にして
ください。このオプションはデータベースのビューとテーブルを Django の
コントロール下に入れずにモデリングするのに役立ちます。Meta.proxy=True
を使ってください。このように設定
すると、データがセーブされた時にプロキシモデルが元のモデルの構造を保
持したままの正確なコピーとなります。Python のサブクラスと同様、 Django のモデルも複数の親モデルクラスを継承でき ます。クラス内の名前解決には、 Python の通常の名前解決規則が適用されるので 注意してください。子クラスで特定の名前 (例えば Meta) を参照する場合、その名前を定義している最初のベースクラスの定義を使い、最初 に名前が見つかった時点で、それ以降同じ名前のオブジェクトの解決は行われませ ん。従って、複数の親クラスで別々に Meta クラスを定義 していても、最初のベースクラスの Meta だけが使われ、 それ以外は全て無視されます。
通常は、モデルの多重継承は必要ないでしょう。多重継承が便利なのは、主に特定 のフィールドやメソッドを追加するための ‘’mix-in’’ クラスを使う場合です。 継承の階層構造はできるだけ単純に、分かりやすくしておきましょう。さもないと、 子クラスで扱っている情報が、どの親クラスから来たか調べるために四苦八苦する はめになるでしょう。
通常の Python クラスの継承では、子クラスで親クラスのどんな属性でもオーバラ
イドすることができます。 Django では、 Field
イ
ンスタンスの属性は (少なくとも今のところは) オーバライドすることができませ
ん。もし親クラスが author
というフィールドを持っていたとして、親クラス
を継承したどんなクラスの中でも新しく author
フィールドを作ることはでき
ません。
親モデルのフィールドをオーバライドすることは、インスタンスの初期化 (どのフ
ィールドが Model.__init__
で初期化されるかを指定すること) 、そしてシリ
アライズを困難なものにします。これらは通常の Python クラスの継承が全
く同じように扱う必要のない特徴です。つまり Django のモデル継承と Python ク
ラスの継承との違いは決定的なものなのです。
この制限は Field
インスタンスである属性のみに適
用されます。通常の Python 属性は望み通りにオーバーライドできます。また、こ
の制限は Python が見ている属性の名前のみに適用されます。自身の手でデータベ
ースのカラム名を指定するのなら、多テーブルの継承向けに同じカラム名を先祖と
子のクラス両方に登場させることもできます (二つの異なるデータベーステーブル
のカラムとして)
先祖のモデルの中にあるモデルフィールドをオーバーライドしようとすると、
Django は FieldError
を生じさせます。
Oct 26, 2017