revision-up-to: | 8961 (1.0) |
---|
モデルとは、サイトを構成するデータの、ただ一つかつ最終的なデータソースを指 します。モデルには、保存したいデータに不可欠なデータフィールドと、その振舞 いが収められています。一般的に、各モデルは単一のデータベーステーブルに対応 づけられています。
基本として、まず以下のことを知っておきましょう:
See also
このドキュメントを理解する上で、 モデル例の公式リポジトリ が参考になるでしょう。 (Django のソース配布物中では、これらの例は tests/modeltests ディレクトリに収められています。)
以下のモデル例では、 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
);
以下に技術的な注意点を示します:
モデルを定義したら、そのモデルを 使う と Django に教えます。設定ファイル を編集して、 models.py の入ったモジュールの名前を INSTALLED_APPS 設定に追加してください。
例えば、アプリケーションのモデルが mysite.myapp.models モジュール ( 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 はフィールドクラス型を以下のいくつかの 判定に使います:
Django にはたくさんの組み込みフィールド型が付属しています。全フィールドのリ ストは モデルフィールドリファレンス を参照してく ださい。組み込みのフィールドで実現できないことがあれば、独自にフィールドを 定義できます カスタムのモデルフィールド を参照してください。
各フィールドには、それぞれフィールド固有の引数がいくつかあります(詳しくは モデルフィールドリファレンス を参照してください)。 例えば、 CharField (とそのサブクラス) は、データ を保存するときの VARCHAR データベースフィールドの長さを指定するために、 必須の引数 max_length が必要です。
また、全てのフィールドに共通の引数もあります。共通の引数は全てオプションで、 詳しくは リファレンス で説明しています。 ここでは、良く使われる引数を簡単に紹介しましょう:
True にすると、フィールドの値を空白 (blank) にできます。デフォ ルト値は False です。
null とは違うことに注意してください。 null が純粋にデータベース上の表現に関わる概念である のに対し、 blank とは値の検証 (validation) に関わ る概念です。あるフィールドに blank=True を指 定すると、 Djangoの adminサイト上で、空の値のエントリを作れるように なります。 blank=False にすると、そのフィール ドには必ず値を入れねばなりません。
2 要素のタプルからなる iterable (リストまたはタプル) を、フィールド の値の選択肢にします。この値を指定すると、 Django の admin には標準 的なテキストフィールドの代わりに選択ボックスが表示され、指定された 選択肢だけをえらべるようになります。
選択肢リストは以下のようになります:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
True を指定すると、フィールドをモデルの主キーにします。
モデルのどのフィールドにも primary_key=True を指定しなければ、Django は自動的に IntegerField を追加して主キーを保存します。従って、デフォ ルトの主キーの振舞いを変更したいのでないかぎり、 primary_key=True をフィールドに指定して おく必要はありません。詳しくは 自動的な主キーフィールド を参照してください。
前にも述べたように、ここではよく使われるフィールドオプションを簡単に説明す るにとどめます。全てのオプションの詳細な情報は 共通のモデルフィールドオプションのリファレンス を参照してください。
デフォルトでは、 Django は各モデルに以下のフィールド:
id = models.AutoField(primary_key=True)
を追加します。これは自動的に値をインクリメントして追加してゆくフィールドで、 主キーに使われます。
カスタムの主キーを指定したければ、いずれかのフィールドに primary_key=True を指定してください。 Field.primary_key の設定を見つけると、Django は id カラムを自動 的に追加しません。
各モデルには必ず一つ primary_key=True のフィー ルドが必要です。
各フィールドの型は、 ForeignKey, ManyToManyField および OneToOneField を除き、オプションの固定引 数 (positional argument) を第一引数として指定できます。この引数には人間可読 なカラム名を指定します。引数を指定しなければ、 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 ではよく使われるデータベースリレーション (relationship)、 多対一 (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 の admin を使っている場合、 ManyToManyField インスタンスを入れておく のは、 admin インタフェースで編集される側のオブジェクトにしておきます。上の 例では、 topping は (Topping に ManyToManyField の pizzas をもたせる のではなく) Pizza に入れておきます。というのも、「トッピングを乗せるピ ザ」と考えるよりも「ピザの上に乗せるトッピング」を考える方が自然だからです。 こうして上の例では、 Pizza の admin フォームでユーザにトッピングを選ば せることになります。
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)
中間モデルでは、多対多のリレーションの両側にあたるモデルに対して明示的に外 部キーを張らねばなりません。この明示的な定義によってはじめて、モデルのリレー ションが宣言されます:
中間モデルには、いくつか制約があります:
さて、これで中間モデル (例でいうところの 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 = [...] のような) 代入によるリレーションの定義 は行えません:
# THIS WILL NOT WORK >>> beatles.members.add(john) # NEITHER WILL THIS >>> beatles.members.create(name="George Harrison") # AND NEITHER WILL THIS >>> beatles.members = [john, paul, ringo, george]
なぜでしょうか。中間モデルでリレーションを作成すると、単に Person と Group の間のリレーションを生成するだけではなく、 Membership テーブ ルに必要な全ての情報が必要です。単に add や create を呼び出したり 代入を行うだけでは、リレーション以外の情報を埋められません。その結果、中間 モデルを使う場合は、 add や create は無効化されるのです。中間モデル を使った多対多のリレーションを定義するには、中間モデルのインスタンスを生成 するしかない、ということを覚えておいてください。
同じ理由で、 remove メソッドも無効化されています。ただし、 clear() メソッドを使えば、以下のように多対多のリレーションを一括して消去できます:
# Beatles have broken up >>> beatles.members.clear()
中間のモデルを使って多対多のリレーションを確立したら、リレーションをまたい だクエリを実行できます。通常の多対多リレーションと同じく、リレーション先の モデルの属性を使ってクエリを実行します:
# 'Paul' で始まる名前の人がメンバにいるグループを探す >>> Groups.objects.filter(person__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]
一対一のリレーションを定義するには、 OneToOneField を使います。このフィールド は他のフィールド型と同じように、モデルのクラス属性に含めて使えます。
このリレーションがもっとも有用なのは、あるオブジェクトが別のオブジェクトを 何らかの形で「拡張」している場合の主キーとして使う場合です。
OneToOneField には固定引数が一つあり、リ レーションを張る対象のクラスを指定します。
例えば、「場所 (place)」のデータベースを作るときには、アドレス、電話番号、 といった標準的なものを作成します。その上で、レストランデータベースを作成 するときに、 Restaurant モデルの中に同じフィールドをつくるような 繰り返し作業 (repeat yourself) をする代わりに、場所データベースを基盤にして レストランのデータベースを作成したいとしましょう。このとき、 Restaurant には Place への OneToOneField をもた せられます (レストランは場所に対して "is-a" の関係だからです。実際には、こ のような状況を扱うのに モデルの継承 を使いますが、 モデル継承は OneToOneField で実現されてい ます)。
ForeignKey と同様、 再帰的リレーション や 未定義のモデルに対するリレーション も定義できま す。詳しくは モデルフィールドのリファレンス を参照してください。
See also
詳細な例は 一対一のリレーションを持つモデルの例 を参照してください。
OneToOneField フィールドは、オプションの引数を一 つとります。引数は モデルフィールドリファレンス で解 説しています。
以前は、モデル中で OneToOneField を使うと、 そのフィールドは自動的にモデルの主キーになっていました。この仕様はもうなく なりましたが、主キーにしたければ手動で primary_key に指定できます。この 変更により、一つのモデル中で複数の OneToOneField を使えるようになりました。
あるモジュールから別のモジュールへのリレーションは全く問題なく張れます。リ レーションを張るには、モデル定義の先頭でリレーション対称のモデルを import しておき、必要なところでそのモデルクラスを参照します。例えば:
from mysite.geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(ZipCode)
Django がモデルのフィールド名に課している制約は二つしかありません:
フィールド名は Python の予約語であってはなりません。さもなければ Python のシンタクスエラーを引き起こすからです。例えば:
class Example(models.Model): pass = models.IntegerField() # 'pass' is a reserved word!
フィールド名には二つ以上の連続するアンダースコアを入れてはなりません。 なぜなら、Django のクエリ照合構文で使われているからです。例えば:
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
フィールドの名前は必ずしもデータベースのカラム名と一致していなくてもよいの で、これらの制約は回避可能です。詳しくは後述の db_column を 参照してください。
join や where, select のような SQL の予約語は、モデルのフィール ド名に使っても かまいません 。というのも、Django はいかなる SQL クエリ文 でも全てのデータベーステーブル名やカラム名をエスケープするからです。エスケー プにはデータベースエンジン固有のクオートシンタクスを使います。
既存のモデルフィールドが目的とするアプリケーションに合わない場合や、あまり 一般的でないデータベースカラムタイプを活用したい場合のために、独自のフィー ルドクラスを作成できます。独自のフィールドの作成方法の詳細は カスタムのモデルフィールド で説明しています。
モデルにメタデータを指定するには、以下のようにモデルの内部クラス 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 表現」を返す、Python の「マジックメソッド」 です。 Python や Django はこのメソッドを使ってモデルインスタンスに 型強制を適用し、通常の文字列に変換したり表示したりしています。特に、 対話コンソールや admin でオブジェクトを表示するときに使われています。
このメソッドは常に定義するよう勧めます。定義しておくと、いろいろと 便利だからです。
このメソッドを定義しておくと、 Django にオブジェクトの URL を計算さ せられます。 Django は admin インタフェースをはじめとして、オブジェ クトから URL を特定する必要があるときにこのメソッドを使います。
オブジェクトを一意に特定できるような URL を持たせたいときには、この メソッドを定義しましょう。
他にも、データベースの動作をカプセル化していて、カスタマイズ可能な モ デルメソッド があります。特によく変更するのは、 save() や delete() の動作でしょう。
これらのメソッド (や、他のモデルメソッド) は、モデルのふるまいを変更するた めに自由にオーバライドしてかまいません。
組み込みメソッドをオーバライドする古典的なユースケースは、オブジェクトを保 存する際に何か別の処理を行うというものです。例を示しましょう (save() のパラメタはドキュメントを参照してください。):
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, force_insert=False, force_update=False):
do_something()
super(Blog, self).save(force_insert, force_insert) # 「実際の」 save() を呼び出します。
do_something_else()
また、以下のようにすれば保存を抑止できます:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, force_insert=False, force_update=False):
if self.name == "Yoko Ono's blog":
return # Yoko は自分のブログを持っていません!
else:
super(Blog, self).save(force_insert, force_update) # Call the "real" save() method.
スーパクラスのメソッド呼び出し、つまり super(Blog, self).save() を忘れ ないでください。この呼び出しを忘れると、デフォルトの動作が行われないため、 データベースにデータが保存されません。
もう一つよくあるパターンは、カスタムの SQL 文をモデルメソッドやモジュールレ ベルのメソッドにに書くというものです。まず、現在のデータベース接続を表現す る django.db.connection オブジェ クを import します。このオブジェクトを使うには、 connection.cursor() を呼び出してカーソルオブジェク トを作成し、 cursor.execute(sql, [params]) で SQL 文を実行して、 cursor.fetchone() や cursor.fetchall() で結 果のレコード行を取り出します。例えば:
def my_custom_sql(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
return row
connection や は、 PEP 249 で定義されてい る標準的な Python DB-API のほぼ全てに加えて、 Django の トランザクショ ン処理 を実装しています。 Python DB-API にあまり 詳しくないのなら、パラメタつきの SQL を実行するときに、SQL 文にパラメタを直 接指定して実行するのではなく、 cursor.execute() の SQL 文内でプレースホルダ "%s" を使うことに注意してください。このテクニックを使えば、データベース ライブラリの中で自動的にクオート処理が行われます。 (また、 Django の使って いるプレースホルダ "%s" は、 SQLite の Python バインディングで使われて いる "?" と違うということにも注意しましょう。この設計は、一貫性と安全性 に配慮して決められています。)
最後に、カスタムの WHERE 節を持つ SQL を実行したいだけなら、 extra() 照合メソッドを使えることにも注意しておきましょう。
Django のモデルクラス継承は、 Python の通常のクラス継承とほぼ同じように動作 します。モデル継承を実現するには、一つだけきめておかねばならないことがあり ます。それは、親クラスに独自のデータベーステーブルを持たせるか、親クラスを 単なるホルダとして使い、子クラスで共通の情報を持たせるだけにするかです。
複数の子モデルクラスでいちいち同じ情報を入力せずに済ませるために、親モデル クラスに共通の情報を持たせたいことはよくあります。親モデルクラスを単体で使 うことがないのなら、 抽象ベースクラス を使うのがよいでしょう。 一方、(他のアプリケーションなどにある) 既存のモデルをサブクラス化して拡張し たり、個々のモデルに固有のデータベースを持たせたいような場合には、 マルチテーブル継承 を使うのがよいでしょう。
抽象ベースクラスは、たくさんのモデルに共通する情報を入れておきたいときに便 利な仕組みです。抽象ベースクラスを定義するには、ベースクラス定義の、 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 レベルに切り出しながらも、データベース レベルでは、各子クラスに一つのデータベーステーブルを生成します。
抽象ベースクラスを作成するとき、 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.filter(name="Bob's Cafe")
# Bob's Cafe が Restaurant オブジェクトなら、子クラスを返す:
>>> p.restaurant
<Restaurant: ...>
ただし、上の p が Restaurant クラスで ない のなら (Place から 直接生成されたオブジェクトや、他のクラスの親クラスなら) p.restaurant はエラーを引き起こします。
マルチテーブル継承では、子クラスが親クラスの Meta ク ラスを継承する意味がありません。 Meta オプションは全 て親クラスだけに適用されるべきもので、他のクラスに同じ内容を適用しても、矛 盾した振る舞いを引き起こすだけです(これは、独自のテーブルを持たない抽象ベー スクラスと対照的な部分です)。
従って、マルチテーブル継承の子クラスは親クラスの Meta クラスにアクセスする方法を持ちません。ただし、限られたケースで、子クラスが 親クラスの挙動を継承する場合があります。それは、子クラスで django.db.models.Options.ordering または django.db.models.Options.get_latest_by 属性を指定していない場合に、 親クラスの属性を継承するというものです。
親クラスで整列カラムが指定されていて、子クラス側で整列カラムを指定したくな い場合は、明示的に空の整列カラムを指定してください:
class ChildModel(ParentModel):
...
class Meta:
# 親クラスの整列カラム設定の効果を除去する
ordering = []
マルチテーブル継承は親クラスと子クラスを暗黙の OneToOneField でリンクするので、前述の例 のように、親クラスから子クラスをたどれます。しかし、子クラスをたどるときに 使う名前は、 ForeignKey や ManyToManyField リレーションを定義したと きのデフォルトの related_name と同じ値 を使っています。従って、子クラスで ForeignKey や ManyToManyField を別のクラスに向けて張る ときには、 必ず related_name をフィー ルドに指定せねばなりません。指定し忘れると、 Django は manage.py 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 のモデルも複数の親モデルクラスを継承でき ます。クラス内の名前解決には、 Python の通常の名前解決規則が適用されるので 注意してください。子クラスで特定の名前 (例えば Meta) を参照する場合、その名前を定義している最初のベースクラスの定義を使い、最初 に名前が見つかった時点で、それ以降同じ名前のオブジェクトの解決は行われませ ん。従って、複数の親クラスで別々に Meta クラスを定義 していても、最初のベースクラスの Meta だけが使われ、 それ以外は全て無視されます。
通常は、モデルの多重継承は必要ないでしょう。多重継承が便利なのは、主に特定 のフィールドやメソッドを追加するための ''mix-in'' クラスを使う場合です。 継承の階層構造はできるだけ単純に、分かりやすくしておきましょう。さもないと、 子クラスで扱っている情報が、どの親クラスから来たか調べるために四苦八苦する はめになるでしょう。
Aug 31, 2012