Django v1.0 documentation

カスタムのモデルフィールド

revision-up-to:8961 (1.0)
Django 1.0 で新たに登場しました.

はじめに

モデルリファレンス ドキュメントでは、 CharFieldDateField といった Django の標準のフィールドクラ スの使用法について説明しています。ほとんどの用途には、これらの標準フィール ドクラスだけで十分でしょう。とはいえ、現行の Django が自分の要求をうまく満 たしていない場合や、 Django が提供しているものと全く異なるフィールドを使い たい場合もあるでしょう。

Django の組み込みフィールド型は、データベースのカラムとして利用できる全ての データ型をサポートしているわけではなく、 VARCHARINTEGER のよう な一般的な型しかサポートしていません。幾何多角形 (geographic polygon) 型や、 PostgreSQL カスタム型 のようなユーザ定義型といった特異な型を扱うには、 Field 型のサブクラスを定義する必要があります。

Field 型のサブクラス定義には、別の用途もあります。複雑な Python オブジェ クトをシリアライズして、標準的なデータベースのカラム型に変換して保存したい 場合です。こうした場合にも、 Field のサブクラスを定義しておき、モデルで 使うと便利です。

Our example object

カスタムフィールドを作成するには、ちょっと注意が必要です。説明についていけ るように、このドキュメントでは一貫して一つの例題を扱うことにします。 ここでは、まず ブリッジ の「手」を表す Python オブジェクトを考えましょう。 (ブリッジの遊び方をしらなくても、心配することはありません。知っておくべきな のは、ブリッジは、 52 枚のカードを 4 人のプレイヤーで扱う遊びで、プレイヤー は伝統的に north, east, south, west と呼ばれることだけです。) Hand クラスは以下のように定義されています:

class Hand(object):
    def __init__(self, north, east, south, west):
        # パラメタはカードのリスト ('Ah', '9s', など)
        self.north = north
        self.east = east
        self.south = south
        self.west = west

    # ... (他のメソッドはここでは省略します) ...

Hand はただの普通の Python クラスで、 Django 固有の定義は一切していませ ん。このクラスを、モデルで扱えるようにしましょう (モデルの hand という 属性に Hand のインスタンスが入るようにします):

example = MyModel.objects.get(pk=1)
print example.hand.north

new_hand = Hand(north, east, south, west)
example.hand = new_hand
example.save()

上の例では、 hand 属性を、他の Python クラスと同様に代入や値参照に使っ ています。このようなことができるトリックは、 Django に Hand オブジェク トの保存やロードの方法を教えることで実現できます。

モデルから Hand クラスを扱うために、 Hand に手を加える必要は 全くありません 。その方が、例えば既存のクラスでソースコードに手を加えら れないような場合にも、簡単にモデル上でサポートできるからです。

Note

単に、カスタムのデータベースカラム型を使って、文字列や浮動小数点型といっ た標準の Python データを扱いたいという場合もあるでしょう。このケースも Hand の例とほぼ同様で、違いは後で説明します。

基本的な考え方

データベースストレージ

モデルフィールドの役割とは、簡単に言えば、文字列、ブール型、 datetime 型、そして Hand のようなより複雑なデータ型の Python オブジェクトを 受け取って、データベースを操作するときに便利な形式に変換することです。 (シリアライザ用にも変換しますが、後で説明するように、一度データベース側の変 換ができるようになれば、シリアライザ用の変換はかなり簡単です)。

モデル内のフィールド値は、データベース上では何らかのカラムタイプに変換され ねばなりません。データベースはバックエンド毎に固有の異なったカラム型を提供 しています。

Hand の例の場合には、プレイヤーのカード情報を、予め決まった順番、 north, east, south, west の順番にくっつけて、 104 文字の文字列に変 換します。従って、 Hand オブジェクトはデータベースにテキスト型はキャラ クタ型で保存できます。

フィールドクラスの役割

Django のフィールド型 (このドキュメントで フィールド という場合には、基本 的に フォームフィールド ではなくモデルフィールド を指します) は、全て django.db.models.Field のサブクラスです。 Django がフィールドごとに記録する情報は、ほとんどのフィールドで共通で、名前、 ヘルプテキスト、バリデータのリスト、ユニークキーにするか、といったものです。 こうした情報の保存は``Field`` クラスで行います。 Field の動作については、 後で詳細に説明しますが、さしあたっては、全ては Field から継承していて、 その中からクラスの挙動の鍵になる部分をカスタマイズするのだと考えておけば十 分です。

Django のフィールドクラスは、モデルの属性値を保存するためのオブジェクトでは ない、ということをよく理解しておいてください。モデルの属性値には、通常の Python オブジェクトが入っています。モデル内でフィールドを定義すると、その フィールドクラスは、モデルクラスの生成時に内部クラス Meta の中に保存さ れます (詳しい動作の仕組みは、ここではあまり重要ではありません)。モデルイン スタンスの属性値を作成したり、変更する時には、フィールドクラスは必要ないの です。その代わり、フィールドクラスは、属性の値と、データベースに保存したり、 シリアライザ に送信したりするための値との間の 変換機構を提供しています。

カスタムのフィールドクラスを定義する際には、上記のことを心に留めておいてく ださい。 Django の Field サブクラスは、 Python オブジェクトとデータベー スやシリアライザ向けの値との変換を色々な方法で実現するための機構を提供しま す (例えば、データを保存するための変換と、照合に使うための変換には違いがあ ります)。この仕組みがトリッキーに見えても、心配する必要はありません。以下の 例を読み進めていくうちにはっきり分かるでしょう。ただ、たいていの場合、カス タムフィールドが必要な場面では、以下の二つのクラスを定義することになるとい うことを覚えておいてください:

  • 一つ目は、ユーザが操作する Python オブジェクトです。このオブジェクト はモデルの属性値として代入され、表示時に読み出されます。例でいうと Hand クラスです。
  • もう一つは Field のサブクラスです。このクラスには、上のクラスと 永続ストレージとの間で相互変換を行うための処理が組み込まれています。

フィールドのサブクラスを定義する

Field のサブクラスを定義するときには、まずは作り たいフィールドが既存のフィールドクラスのどれに一番近いかを考えましょう。

既存の Django のフィールド型をサブクラス化すれば、作業が楽になるでしょうか? もしそうでなければ、 Field クラスをサブクラス化 します。

フィールドの初期化時には、 Field クラス (または 親クラス) 共通の引数と、新たなフィールドクラス固有の引数を分離して、前者を 親クラスの __init__() に渡します。

このドキュメントの例題では、定義するフィールドを HandField と呼ぶことに しましょう (フィールドを (何とか)Field と呼ぶようにしておけば、クラスが Field のサブクラスであるとすぐ分かるからです)。 HandField は既存のフィールドと似た部分はあまりないので、 Field クラスから直接導出します:

from django.db import models

class HandField(models.Field):
    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 104
        super(HandField, self).__init__(*args, **kwargs)

HandField は標準的なフィールドオプションのほとんどを指定できます。ただ し、このフィールドは高々 52 枚のカードの値と種類だけを保存できればよいので、 合計 104 文字分の固定長にしておきます。

Note

多くの Django のモデルフィールドは、そのフィールドに関係のないオプショ ンも引数に指定できます。例えば、 editableauto_now の両方を DateField に指定してもよく、 DateField は (auto_now を指定すると editable=False なので) editable パ ラメタを無視します。この場合、エラーは送出されません。

このような挙動にしておくと、不要なオプションをいちいちチェックしなくて よいので、フィールドクラスの定義が簡単になります。サブクラスでは、単に 全てのオプションを親クラスに渡しておけばよく、それ以上何もしなくてよい のです。選べるオプションを厳しく制限してもよいですし、簡単にしたければ、 何でも指定できるようにしておけばよいのです。

__init__() メソッドは、以下のパラメタを (列挙 順に) 取ります:

  • verbose_name
  • name
  • primary_key
  • max_length
  • unique
  • blank
  • null
  • db_index
  • core
  • rel: (ForeignKey のような) リレーションに関するフィールドで使います。高度な用途でしか必要ありま せん。
  • default
  • editable
  • serialize: False の場合、モデルイ ンスタンスを シリアライザ に渡しても、 フィールドをシリアライズしません。デフォルトの値は True です。
  • prepopulate_from
  • unique_for_date
  • unique_for_month
  • unique_for_year
  • validator_list
  • choices
  • help_text
  • db_column
  • db_tablespace: 現状は Oracle バックエ ンドだけで使っています。インデクス生成に使います。通常、このオプショ ンは無視できます。

上のリストで解説のないオプションは、通常の Django のフィールドと同じ意味を 持っています。詳しくは モデルのドキュメント を参 照してください。

SubfieldBase メタクラス

はじめに でも説明したように、フィールド型のサブクラスが必要な理由は主に 二つあります。一つはカスタムのデータベースカラム型を利用したい場合、もう一 つは複雑な Python のデータ型を扱いたい場合です。もちろん、二つを合わせて実 現するのも可能です。カスタムのデータベースカラム型を扱っているだけで、かつ Python からモデルフィールドを扱うときのデータ型が、データベースバックエンド から取り出したデータの型と一致している場合には、この節を気にする必要はあり ません。

Hand クラスのようにカスタムの Python 型を扱うフィールドを作成したい場合、 Django がモデルのインスタンスを生成して、フィールドの属性値をデータベースに 保存する時に、フィールドの値を適切な Python オブジェクトに変換する必要が あります。この処理のからくりはやや複雑ですが、フィールドクラスを定義すると きに書かねばならないコードはいたって単純で、特殊なメタクラスを使ってサブク ラスを作成するだけです:

class HandField(models.Field):
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        # ...

こうしておけば、属性値を初期化する際に、自動的に to_python() メソッド が呼び出されるようになります。 to_python() メソッドについては後で解説 します。

便利なメソッド

Field のサブクラスを作成して、 __metaclass__ をセットアップしたら、次はフィールドの挙動に応じて標準メ ソッドをいくつかオーバライドします。以下のメソッドは上からよく使う順に挙げ ています。

データベース型のカスタマイズ

db_type(self)

DATABASE_ENGINE 設定に指定されているデータベースで、フィールドの データを保存するために使うカラム型を返します。

例えば、 PostgreSQL 固有のカスタム型 mytype を作成したとしましょう。 Django からこの型を使うには、以下のように Field をサブクラス化して、 db_type() メソッドを実装します:

from django.db import models

class MytypeField(models.Field):
    def db_type(self):
        return 'mytype'

MytypeField を定義したら、他のフィールド型と同じようにモデルで利用でき ます:

class Person(models.Model):
    name = models.CharField(max_length=80)
    gender = models.CharField(max_length=1)
    something_else = MytypeField()

データベースに依存しないアプリケーションを構築したいなら、データベース毎 のカラム型の違いに注意しておかねばなりません。例えば、 PostgreSQL では日付 や時間に関するカラム型は timestamp 型ですが、 MySQL では datetime 型です。この違いを db_type() メソッドの中で吸収するには、Djangoの settings モジュールを import しておいて、以下のように DATABASE_ENGINE 設定をチェックします:

class MyDateField(models.Field):
    def db_type(self):
        from django.conf import settings
        if settings.DATABASE_ENGINE == 'mysql':
            return 'datetime'
        else:
            return 'timestamp'

db_type() メソッドは、Django がアプリケーションのテーブルを生成する ための CREATE TABLE` 文を構築する瞬間、すなわち最初にテーブルを生成する 瞬間しか呼び出されません。それ以外の場合に呼び出されることはないので、上記 の DATABASE_ENGINE をチェックする例のような多少ややこしいコード を書いてもさして問題ではありません。

カラムのデータ型にはパラメタを取るものがあります。例えば CHAR(25) は、 25 がカラムの最大長を表しています。こうした場合には、 db_type() メソッド内でハードコードしておくよりも、モデル内でパラメタを指定して使える 方が柔軟性を高められます。例えば、以下に示すような CharMaxlength25Field はあまり便利ではありません:

# パラメタをハードコードしているおバカな例
class CharMaxlength25Field(models.Field):
    def db_type(self):
        return 'char(25)'

# モデル内での使い方
class MyModel(models.Model):
    # ...
    my_field = CharMaxlength25Field()

実行時、すなわちフィールドクラスをインスタンス化する時にパラメタを指定でき た方がよいでしょう。パラメタを指定できるようにするには、以下のように django.db.models.Field.__init__() を実装します:

# より柔軟性のある例
class BetterCharField(models.Field):
    def __init__(self, maxlength, *args, **kwargs):
        self.max_length = max_length
        super(BetterCharField, self).__init__(*args, **kwargs)

    def db_type(self):
        return 'char(%s)' % self.max_length

# モデル内での使い方
class MyModel(models.Model):
    # ...
    my_field = BetterCharField(25)

最後に、カラムの操作に非常に複雑な SQL が必要な場合には、 db_type()None を返させましょう。そうすれば、 Django の SQL 生成コードはこの フィールドの処理をスキップします。もちろん、その場合には、何らかの方法で正 しいテーブルに正しいカラムを生成する必要があります。

データベース上の値を Python オブジェクトに変換する

to_python(self, value)

データベース (またはシリアライザ) の返す値を Python オブジェクトに変換しま す。

デフォルトの実装では、単に value を返します。通常は、データベースバック エンドは正しい形式 (Python 文字列型など) でデータを返すからです。

カスタムのフィールドクラスで、文字列や日時、整数や浮動小数点型といったデー タ型よりも複雑なデータ構造を扱いたい場合、このメソッドをオーバライドする必 要があります。一般的な規則として、このメソッドは以下の引数をきちんと扱えね ばなりません:

  • 正しい型のインスタンス (このドキュメントの例では Hand オブジェク ト)
  • 文字列 (デシリアライザなどが返す値)
  • このフィールドのカラムタイプに対して、データベースバックエンドが返す 値

HandField クラスでは、データを VARCHAR フィールドで保存しているので、 to_python() は文字列と Hand インスタンスの両方を扱えねばなりません:

import re

class HandField(models.Field):
    # ...

    def to_python(self, value):
        if isinstance(value, Hand):
            return value

        # The string case.
        p1 = re.compile('.{26}')
        p2 = re.compile('..')
        args = [p2.findall(x) for x in p1.findall(value)]
        return Hand(*args)

このメソッドは常に Hand インスタンスを返していることに注意してください。 to_python() は、モデルの属性値として保存したい Python オブジェクト型を 返します。

忘れないで!: カスタムフィールドに対して to_python() を呼び出した
ければ、上で述べた SubfieldBase メタクラス を使わねばなりません。メタ クラスを使わないと、 to_python() は自動的に呼び出されません。

Python オブジェクトをデータベース保存用の値に変換する

get_db_prep_value(self, value)

to_python() の逆のメソッドで、データベースバックエンドを扱うときに呼 び出されます。 value パラメタは、モデルの現在の属性値です (このような設 計になっているのは、個々のフィールドが、自身を組み込んでいるモデルに対する 参照を持っていないため、フィールドが自分でモデルインスタンスから値を取り出 せないからです)。 このメソッドは、データベースバックエンドのクエリパラメタ として使えるようにデータをフォーマットして返さねばなりません。

例を示します:

class HandField(models.Field):
    # ...

    def get_db_prep_value(self, value):
        return ''.join([''.join(l) for l in (value.north,
                value.east, value.south, value.west)])
get_db_prep_save(self, value)

前述のメソッドと同じですが、フィールドの値をデータベースに 保存 せねばな らないときに呼び出されます。デフォルトの実装では単に get_db_prep_value を呼び出しています。従って、データベースにデータを保存するときに、 (get_db_prep_value で実装している) 通常のクエリパラメタと違うものを使わ ねばならないような特殊な状況を除き、このメソッドを実装する必要はありません。

保存前に値に前処理をほどこす

pre_save(self, model_instance, add)

このメソッドは、 get_db_prep_save() の直前に呼び出され、このフィール ドの model_instance での適切な値を生成して返さねばなりません。属性の名 前は self.attrname で参照できます (この値はフィールドのインスタンス化 時に設定されます)。モデルインスタンスをデータベースに初めて保存しようとして いる場合、 add パラメタの値は True です。それ以外の値は False です。

このメソッドをオーバライドする必要があるのは、フィールドの値を、保存の直前 に前処理したい場合だけです。例えば、 Django の DateTimeField は、 auto_nowauto_now_add で生成された場合、このメソッド を使って正しい値をセットします。

このメソッドをオーバライドする場合、最後に属性の値を返さねばなりません。 また、フィールドの値を変更した場合、モデル上の対応する値も更新して、モデル インスタンスを参照しているコードが正しい値を読み出せるようにせねばなりませ ん。

データベース照合時のパラメタを SQL 用に変換する

get_db_prep_lookup(self, lookup_type, value)

データベース上で照合を行う時 (WHERE 制約付きで SQL を発行する時) にデー タベースに渡す値を前処理します。 lookup_type は、 Django のフィルタ照合 条件である exact, iexact, contains, icontains, gt, gte, lt, lte, in, startswith, istartswith, endswith, iendswith, range, year, month, day, isnull, search, regex, iregex のいずれかの値を取ります。

このメソッドをオーバライドする場合、 lookup_type に指定される全ての照合 条件に対応せねばなりません。また、 value が正しい値でない場合 (単一のオ ブジェクトが指定されるべきところにリストが指定された場合など) には ValueError を、フィールドが指定した照合に対応していない場合には TypeError を送出せねばなりません。たいていのフィールドでは、特別な扱い の必要な照合タイプだけを処理して、残りは親クラスの get_db_prep_lookup() メソッドに委ねればよいでしょう。

get_db_prep_save() を定義するようなケースでは、通常 get_db_prep_lookup() も定義する必要があります。定義しなければ、 exact, gt, gte, lt, lte, in, range の照合時には デフォルトの実装に従って、 get_db_prep_value() が呼び出されます。

また、カスタムのフィールド型でサポートする照合タイプを制限したい場合にも、 このメソッドを定義する必要があるでしょう。

rangein を使った照合を行う場合、 get_db_prep_lookup() は 引数として (適切な型の) オブジェクトのリストを受け取り、データベースに渡せ るなにがしか適切な型のオブジェクトからなるリストに変換せねばなりません。 大抵の場合は get_db_prep_save() を使い回したり、別のメソッドに共通の 処理を括り出しておけます。

例えば、以下のコードでは get_db_prep_lookup() を実装して、照合タイプ を exactin に制限しています:

class HandField(models.Field):
    # ...

    def get_db_prep_lookup(self, lookup_type, value):
        # 'exact' および 'in' だけを扱います。それ以外はエラーにします。
        if lookup_type == 'exact':
            return [self.get_db_prep_value(value)]
        elif lookup_type == 'in':
            return [self.get_db_prep_value(v) for v in value]
        else:
            raise TypeError('Lookup type %r not supported.' % lookup_type)

モデルフィールドに対応したフォームフィールドを指定する

formfield(self, form_class=forms.CharField, **kwargs)

このフィールドをモデルに組み込んで、フォームで表示したときに使うデフォルト のフォームフィールドを返します。このメソッドは、 ModelForm ヘルパから呼び出されます。

kwargs 辞書の内容は、全てフォームフィールドの Field__init__() メソッドに渡されます。通常、このメソッ ドの定義では、適切な form_class 引数のデフォルト値を設定して、それ以降 の処理を親クラスに移譲しているだけです。フォームフィールドを定義する場合、 カスタムのフォームフィールド (や、フォームウィジェット) を書く必要があるか もしれません。カスタムのフォームフィールドについては forms のドキュメント を参照してください。また、 カスタムウィジェットは django.contrib.localflavor のコードを参照する とよいでしょう。

これまでの例に合わせて書くと、 formfield() メソッドは以下のように書け ます:

class HandField(models.Field):
    # ...

    def formfield(self, **kwargs):
        # メソッドの呼び出し側がデフォルトをオーバライド
        # できるようにするための標準的な書き方
        defaults = {'form_class': MyFormField}
        defaults.update(kwargs)
        return super(HandField, self).formfield(**defaults)

上の例では、 MyFormField フィールドクラスを import しているのが前提です (そして、 MyFormField でデフォルトのウィジェットを定義しています)。カス タムのフォームフィールドの定義方法は、このドキュメントでは扱いません。

組み込みのフィールド型をエミュレートする

get_internal_type(self)

データベースレベルでエミュレーションしているフィールド型の名前を返すメソッ ドです。このメソッドは、簡単な方法でフィールド型を定義する際に、データベー スのカラム型を決定するために使われます。

db_type() メソッドを定義していれば、 get_internal_type() を気に する必要はありません。このメソッドは、ほとんど使われません。ただし、フィー ルド型のデータベースストレージが、他のフィールドと同じような型である場合、 このメソッドを定義しておくと、他のフィールドのロジックを使ってカラムを生成 させられます。

例を示します:

class HandField(models.Field):
    # ...

    def get_internal_type(self):
        return 'CharField'

どのデータベースバックエンドを使っているかに関わらず、上のように定義してお くと、 syncdb などの SQL 関係のコマンドを実行した時に、文字列を保存す るための正しいカラム型を生成します。

get_internal_type() が、 Django 上でもデータベースバックエンドでも対 応しない型を示す文字列を返した場合、すなわち、 django.db.backends.<db_name>.creation.DATA_TYPES に存在しない文字列を返 した場合、シリアライザはこの文字列を使いますが、デフォルトの db_type() 実装は None を返します。なぜこのような挙動が有用なのか は、 db_type() のドキュメントを読んでください。シリアライザに、フィー ルドの型名として文字列を指定しておくと、 Django の外の何らかの場面でシリア ライザの出力を扱うときに便利だからです。

シリアライゼーション用にフィールドデータを変換する

value_to_string(self, obj)

このメソッドは、シリアライザがフィールドの値を出力する際に文字列に変換する ために使います。シリアライズデータを生成するには、 Field._get_val_from_obj(obj)() を呼び出すとよいでしょう。例えば、例え ば、 HandField はデータストレージに文字列を使っているので、既存の変換コー ドを再利用できます:

class HandField(models.Field):
    # ...

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

一般的なアドバイス

カスタムフィールドの定義は、とりわけ Python 型とデータベース、そしてシリア ライゼーション形式との間で複雑な変換を行う場合には、トリッキーな処理になり がちです。よりスムーズにフィールドを定義するためのアドバイスを、以下に示し ます:

  1. 既存のフィールド型定義 (django/db/models/fields/__init__.py にあります) を見て、インスピレーションを得ましょう。全く何もない状態 からスクラッチでフィールドを定義するのではなく、自分の実現したいこと に近い処理を行っているフィールド型を探して、それをちょっとだけ拡張す るようにしましょう。
  2. フィールドとしてラップしたいクラスに、 __str__()__unicode__() メソッドを定義しましょう。フィールドの処理におけ るデフォルトの動作として、ラップしているクラスのインスタンスに対して force_unicode() を呼び出すような処理が たくさんあります。 (このドキュメントの例では、 valueHand のインスタンスであり、 HandField のインスタンスではないので) __unicode__() メソッドを定義しておき、 Python オブジェクトが自 動的に文字列形式に変換されるようにしておけば、作業を大幅に減らせます。

FileField のサブクラスを定義する

上に挙げたメソッドに加えて、ファイルを扱うフィールドを定義するときには、い くつか必要な条件があります。データベースストレージの操作やデータの取得など、 フィールド定義に必要なメカニズムのほとんどは FileField で定義しています が、特定のタイプのファイルをサポートするための処理はサブクラスに委ねていま す。

Django はファイルのコンテンツや操作をプロキシする File クラスを提供して います。このクラスはサブクラス化でき、ファイルのアクセス方法やメソッドをカ スタマイズできます。 File クラスは django.db.models.fields.files で 定義されており、デフォルトの動作は ファイルクラスのドキュメント で解説しています。

File のサブクラスを作ったら、 FileField サブクラスの attr_class に指定して、 FileField サブクラスに指定のファイルクラスを使うよう指示し てください。

アドバイス

上に挙げた説明に加えて、カスタムフィールドのコードの効率や可読性を挙げるた めのガイドラインをいくつか示しておきます。

  1. Django の ImageField (django/db/models/fields/files.py) は、 FileField をサブクラス化して特定のファイルタイプをサポートしてい る素晴らしい例題です。このクラスでは、上に述べたテクニックを余すとこ ろ無く使っています。
  2. 可能な限り、ファイルの属性をキャッシュしましょう。ファイルは遠隔のス トレージシステム上にあるので、ファイルデータの取得には時間もお金もか かります。しかも、常に必要なわけではありません。一度ファイルを取得し て、コンテンツに関するデータを収集したら、データ可能な限りキャッシュ して、以後のアクセスでファイルを再取得する回数を減らしましょう。