Django v1.0 documentation

Django admin サイト

revision-up-to:8961 (1.0)

Django の最も強力な機能の一つに、自動生成される admin インタフェースがあり ます。 admin はモデルのメタデータを読み込んで、強力で実運用に耐えるインタ フェースを生成し、コンテンツの製作者がすぐにサイトにコンテンツを投入できる ようにします。このドキュメントでは、 Django の admin インタフェースを有効に したり、カスタマイズしたりする方法を解説します。

注意

admin サイトは Django 0.96 から大幅にリファクタされました。このドキュメ ントでは、よりカスタマイズ機能の充実した新しい admin サイトについて解説 しています。この admin サイトの仕様は、 Django の開発をよく追いかけてい る人なら、一度は “newforms-admin” という名前で耳にしたことがあるはずで す。

概要

Django admin サイトは、以下の 5 つのステップを通して有効化します:

  1. django.contrib.adminINSTALLED_APPS に追加します。
  2. admin インタフェースでの編集を可能にするモデルを決めます。
  3. 2 で決めたモデルに対して、必要なら ModelAdmin クラスを定義します。 ModelAdmin はモデルごとにカスタマイズした admin の機能やオプショ ンをカプセル化しています。
  4. AdminSite をインスタンス化して、モデルや ModelAdmin クラスを 指定します。
  5. AdminSite インスタンスを URLconf にフックします。

ModelAdmin オブジェクト

ModelAdmin クラスは、 admin インタフェース上のモデルを表現しています。 このクラスは、アプリケーションの admin.py という名前のファイルに保存し ます。 ModelAdmin の簡単な例を以下に示します:

from django.contrib import admin
from myproject.myapp.models import Author

class AuthorAdmin(admin.ModelAdmin):
    pass
admin.site.register(Author, AuthorAdmin)

ModelAdmin のオプション

ModelAdmin はとてもフレキシブルなクラスです。このクラスには、 admin イ ンタフェースをカスタマイズするためのオプションがいくつもあります。オプショ ンは、全て、 ModelAdmin のサブクラスで以下のように指定します:

class AuthorAdmin(admin.ModelAdmin):
    date_hierarchy = 'pub_date'

date_hierarchy

date_hierarchy をモデルの DateFieldDateTimeField に指定する と、変更リストのページに、指定フィールドの日付を使って日付ベースで絞り込み できるナビゲーションが組み込まれます。

例:

date_hierarchy = 'pub_date'

form

デフォルトの設定では、モデルに対して ModelForm が動的に生成され、追加/ 変更ページでフォームを生成するときに使われます。 form を独自の ModelForm と置き換えれば、追加/変更ページのふるまいを変更できます。

詳しくは、 admin にカスタムのバリデーションを追加する を参照してください。

fieldsets

admin の「追加 (add)」および「変更 (change)」ページのレイアウトを制御するに は、 fieldsets を使います。

fieldsets は 2 要素のタプルのリストです。各タプルは admin フォームペー ジ上の <fieldset> を表します (<fieldset> はいわばフォームの「セクショ ン」です)。

フィールドセットは (name, field_options) の形式をとります。 name はフィールドセットの名前を表す文字列で、 field_options はフィールドセッ ト内で表示したいフィールドの情報を入れた辞書です。この情報の中に、表示した いフィールドのリストも指定します。

django.contrib.flatpages.FlatPage モデルから抜き出した例を示します:

class FlatPageAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {
            'fields': ('url', 'title', 'content', 'sites')
        }),
        ('Advanced options', {
            'classes': ('collapse',),
            'fields': ('enable_comments', 'registration_required', 'template_name')
        }),
    )

このフィールドセットによって、 admin のページは以下のようになります:

../../_images/flatfiles_admin.png

fieldsets を指定しない場合、 Django は AutoField でなく、かつ editable=True であるようなフィールドを、モデルに定義した順番に個別のフィー ルドセットとして表示します。

field_options 辞書には以下のようなキーを指定できます:

  • fields フィールドセット内に表示するフィールド名からなるタプルです。 必須のキーです。

    使い方:

    {
    'fields': ('first_name', 'last_name', 'address', 'city', 'state'),
    }
    

    同じ行に複数のフィールドを表示したい場合、それらのフィールドをタプ ルでラップして入れます。下の例では、 first_namelast_name が同じ行に表示されます:

    {
    'fields': (('first_name', 'last_name'), 'address', 'city', 'state'),
    }
    
  • classes

    フィールドセットに適用される追加の CSS クラス名です。

    使い方:

    {
    'classes': ['wide', 'extrapretty'],
    }
    

    admin サイトのデフォルトのスタイルシートで定義されている便利なクラ スとして collapsewide があります。 collapse スタイ ルのフィールドセットは、 admin ページでは最初折り畳まれ (collapse) ており、小さな"クリックして展開 (click to expand)" リンクに置き換わっ ています。 wide スタイルのフィールドセットには水平方向に追加のス ペースが加わります。

  • description

    各フィールドセットの先頭の、ヘッダのすぐ下に表示する追加の文字列で、 オプションです。

    この文字列は admin 上で HTML エスケープ されません 。そのため、必 要ならそのまま HTML を入れられます。また、 HTML 固有の文字をエスケー プしたければ、 django.utils.html.escape() を使ってください。

fields

レイアウトを気にせず、単にモデルの一部のフィールドだけをフォームに表示した いだけの場合に、 fieldsets の代わりに使ってください。例えば、 django.contrib.flatpages.FlatPage モデルの admin フォームの簡単なバージョ ンを以下のように定義できます:

class FlatPageAdmin(admin.ModelAdmin):
    fields = ('url', 'title', 'content')

上の例では、 'url', 'title', 'content' フィールドだけが順番にフォームに表示 されます。

Note

この fields オプションと fieldsets オプションの中の fields キーとを混同しないでくださいね。

exclude

この属性にフィールドの名前のリストを指定すると、指定したフィールドがフォー ムから除去されます。

例えば、以下のようなモデルがあったとします:

class Author(models.Model):
    name = models.CharField(max_length=100)
    title = models.CharField(max_length=3)
    birth_date = models.DateField(blank=True, null=True)

nametitle フィールドだけを含む Author モデルのフォームを 表示したければ、 fieldsexclude を使って、それぞれ以下のように 定義できます:

class AuthorAdmin(admin.ModelAdmin):
    fields = ('name', 'title')

class AuthorAdmin(admin.ModelAdmin):
    exclude = ('birth_date',)

Author モデルは 3 つのフィールド name, title, birth_date しか持たないので、上の 2 つの例は全く同じフィールドを含むフォームを生成しま す。

filter_horizontal

ユーザビリティが紙一重の <select multiple> の代わりに、気の利いた控えめ な Javascript の「フィルタ」インタフェースを使います。フィルタインタフェー スを横並びにして表示させたいフィールドのリストを指定してください。フィルタ インタフェースを縦並びにしたい場合は filter_vertical を使ってください。

filter_vertical

filter_horizontal とほぼ同じですが、フィルタインタフェースを縦並びで表 示します。

list_display

admin の変更リストページに表示するフィールドを制御するには list_display を使います。

使い方:

list_display = ('first_name', 'last_name')

list_display を指定しなければ、 admin サイトは各オブジェクトの __unicode__() 表現を表示するカラムを一つだけ表示します。

list_display には 4 通りの設定方法があります:

  • モデルのフィールド名:

    class PersonAdmin(admin.ModelAdmin):
        list_display = ('first_name', 'last_name')
    
  • モデルインスタンスを引数にとる呼び出し可能オブジェクト:

    def upper_case_name(obj):
        return "%s %s" % (obj.first_name, obj.last_name).upper()
    upper_case_name.short_description = 'Name'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = (upper_case_name,)
    
  • ModelAdmin の属性名を表す文字列。呼び出し可能オブジェクトと同じよ うに動作します:

    class PersonAdmin(admin.ModelAdmin):
        list_display = ('upper_case_name',)
    
        def upper_case_name(self, obj):
          return "%s %s" % (obj.first_name, obj.last_name).upper()
        upper_case_name.short_description = 'Name'
    
  • モデルの属性名を表す文字列。ただし、 self はモデルインスタンスを 表します:

    class Person(models.Model):
        name = models.CharField(max_length=50)
        birthday = models.DateField()
    
        def decade_born_in(self):
            return self.birthday.strftime('%Y')[:3] + "0's"
        decade_born_in.short_description = 'Birth decade'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('name', 'decade_born_in')
    

list_display にはいくつか特殊なケースがあります:

  • フィールドが ForeignKey の場合、関連づけられているオブジェクトの __unicode__() を表示します。

  • ManyToManyField フィールドの表示は、テーブルの各行に対して個別に SQL 文を実行することになってしまうのでサポートしていません。どうして も表示させたいなら、カスタムメソッドをモデルに実装して、メソッドの名 前を list_display に追加してください (list_display へのカスタ ムメソッドの追加については、後で詳しく説明しています)。

  • フィールドが BooleanFieldNullBooleanField の場合、 TrueFalse の代りに "オン" や "オフ" を示すアイコンを表示 します。

  • モデルや ModelAdmin のメソッド、呼び出し可能オブジェクトの名前を 指定した場合、 Django はデフォルトでメソッドの出力を HTML エスケープ します。メソッドの出力をエスケープしたくない場合には、メソッドの allow_tags 属性の値を True にしてください。

    以下に例を示します:

    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
        color_code = models.CharField(max_length=6)
    
        def colored_name(self):
            return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
        colored_name.allow_tags = True
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('first_name', 'last_name', 'colored_name')
    
  • TrueFalse を返すようなモデルの ModelAdmin のメソッド、 呼び出し可能オブジェクトの名前を指定した返す場合、メソッドの boolean 属性を True に設定しておくと、Django は「オン」や「オ フ」のアイコンを表示します。

    以下に例を示します:

    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        birthday = models.DateField()
    
        def born_in_fifties(self):
            return self.birthday.strftime('%Y')[:3] == 5
        born_in_fifties.boolean = True
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('name', 'born_in_fifties')
    
  • __str__() および __unicode__() メソッドは他のモデルメソッドと 同じように list_display に入れられるので、以下のように書いても全 く問題ありません:

    list_display = ('__unicode__', 'some_other_field')
    
  • 通常、 list_display の要素のうち、実際のデータベースのフィールド に対応していないものは、変更リストページで並び順を変えるときのカラム には使えません。 Django はソートをすべてデータベースレベルで行うから です。

    ただし、 list_display のいずれかの要素が実際にデータベース上のあ るフィールドを指している場合、 admin_order_field という属性を使っ て、 Django にそのことを教えられます。

    例を示しましょう:

    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        color_code = models.CharField(max_length=6)
    
        def colored_first_name(self):
            return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
        colored_first_name.allow_tags = True
        colored_first_name.admin_order_field = 'first_name'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('first_name', 'colored_first_name')
    

    この例では、 Django は Admin 上で colored_first_name を並べ変える 際に first_name フィールドを使います。

list_filter

admin の変更リストページの右側のサイドバーにあるフィルタを有効にするには、 list_filter を設定します。この値はフィールド名のリストにします。 各フィールド名は BooleanField, CharField, DateField, DateTimeField, IntegerField, ForeignKey のいずれかでなければ なりません。

以下の例は django.contrib.auth.models.User モデルからとったもので、 list_displaylist_filter の仕組みを示しています:

class UserAdmin(admin.ModelAdmin):
    list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
    list_filter = ('is_staff', 'is_superuser')

上のコードによって、 admin の変更リストは以下のようになります:

../../_images/users_changelist.png

(この例では、後述の search_fields も定義しています。)

list_per_page

admin 変更リストをページ分割 (paginate) で表示するときに、各ページに何個の アイテムを表示するかを決めます。デフォルト値は 100 です。

inlines

後述の InlineModelAdmin を参照してください。

ordering

ordering を設定すると、 admin の変更リストにおける整列順を指定できます。 値はタプルからなるリストで、モデルの ordering パラメタと同じ形式で指定 します。

この値を指定しない場合、 Django はモデルのデフォルトの整列順を使います。

Note

Django はリストやタプルの最初の要素だけを考慮して、後の要素は無視します。

prepopulated_fields

フィールドの値を別のフィールドの値からセットさせたい場合は、以下のように、 prepopulated_fields にフィールド名を対応付けた辞書を設定してください:

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}

prepopulated_fields をセットすると、フィールドに小さな JavaScript のア クションが設定され、引数に指定したフィールドから値を自動的に取り込みます。 prepopulated_fields は、主に他の複数のフィールドから SlugField フィー ルドの値を生成するときに使います。値は、まず各ソースフィールドの値を結合し て、その結果が有効なスラグになるよう変換 (スペースをダッシュに置換するなど) して生成します。

DateTimeField, ForeignKey および ManyToManyFieldprepopulated_fields に指定できません。

radio_fields

デフォルトでは、Django の admin は ForeignKey のフィールドや choices の設定されたフィールドに対してセレクタボックス (<select>) イン タフェースを使います。 radio_fields にフィールド名を指定しておくと、 Django はセレクタボックスの代りにラジオボタンのインタフェースを使います。 例えば、 groupPerson モデル上の ForeignKey フィールド であれば、以下のように書けます:

class PersonAdmin(admin.ModelAdmin):
    radio_fields = {"group": admin.VERTICAL}

ラジオボタンの並び方を指定するシンボル、 HORIZONTAL または VERTICALdjango.contrib.admin モジュールにあります。

ForeignKeychoices パラメタのセットされたフィールド以外に radio_fields を使ってはなりません。

raw_id_fields

デフォルトでは、Django の admin サイトは ForeignKey フィールドに対して セレクタボックス (<select>) インタフェースを使います。しかし、時にはリレー ション先の全てのオブジェクトの入ったセレクタボックスが生成され、ドロップダ ウン表示されるのを避けたい場合もあります。

raw_id_fields には、ウィジェットを Input に変更したいフィールドのリ ストを指定します。 ForeignKey または ManyToManyField を指定できます:

class ArticleAdmin(admin.ModelAdmin):
    raw_id_fields = ("newspaper",)

save_as

save_as を指定すると、 admin の編集フォームで「別名で保存 (save as)」機 能を使えるようになります。

通常、編集フォームには三つの保存オプション、すなわち「保存 (Save)」、「保存 して編集を続ける (Save and continue editing)」、「保存してもう一つ追加 (Save and add another)」があります。 save_asTrue にすると「保存 してもう一つ追加」は「別名で保存 (Save as)」に置き換わります。

「別名で保存」は、現在のオブジェクトをそのまま保存するのではなく、(新たな ID を持った) 別のオブジェクトとして保存することです。

デフォルトでは、 save_asFalse に設定されています。

save_on_top

save_on_top を指定すると、 admin の変更フォームの最上部に保存ボタンを追 加できます。

通常、保存ボタンはフォームの最下部だけに表示されます。 save_on_top を指 定すると、ボタンは最下部だけでなく最上部にも表示されます。

デフォルトでは、 save_on_topFalse です。

search_fields

search_fields を指定すると、 admin の変更リストページで検索ボックスを使 えるようになります。この値は、ユーザが検索クエリをテキストボックスに入力し たときに検索の対象に含めるフィールド名のリストです。

フィールドは CharFieldTextField のような何らかのテキストフィー ルドでなければなりません。 DB API の「リレーションを追跡する」表記を使えば、 ForeignKey を介したフィールドの指定も行えます:

search_fields = ['foreign_key__related_fieldname']

admin の検索ボックスで検索を実行すると、 Django は検索クエリを単語に分解し て、各単語を含むような全てのオブジェクトを返します。検索は大小文字を区別せ ず、 search_fields に指定したフィールドのうち少なくとも一つに単語が入っ ていればヒットします。例えば、 search_fields['first_name', 'last_name'] に設定されている場合、ユーザが john lennon を検索すると、 Django は以下のような WHERE 節を持った SQL と等価な検索を実行します:

WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%')
AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%')

より高速な、あるいはより制約の厳しい検索を行うには、フィールド名の前に以下 のような演算子を置きます:

^

フィールドの先頭にマッチします。例えば、 search_fields['^first_name', '^last_name'] にして、ユーザが john lennon を検 索した場合、Django は以下のような WHERE 節の SQL に等価な検索を実行 します:

WHERE (first_name ILIKE 'john%' OR last_name ILIKE 'john%')
AND (first_name ILIKE 'lennon%' OR last_name ILIKE 'lennon%')

このクエリを使うと、データベースはカラムの先頭部分だけをチェックすれば よく、カラム中のデータ全体を調べなくてもよくなるため、通常の '%john%' クエリよりも効率的に検索を実行できます。加えて、カラムにイ ンデクスが設定されていれば、データベースによっては LIKE クエリであっ てもインデクスを使ったクエリを実行できるという利点があります。

=

大小文字を区別しない厳密一致です。例えば、 search_fields['=first_name', '=last_name'] にして、ユーザが john lennon を検 索した場合、 Django は以下のような WHERE 節の SQL に等価な検索を実行 します:

WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john')
AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon')

クエリ入力はスペース区切りなので、この例に従うと、 first_name'john winston' である (スペースを含む) ようなレコードは検索でき ないので注意してください。

@
全文検索マッチを実行します。デフォルトの search メソッドに似ていますが、 インデクスを使います。現在のところ MySQL でしか使えません。

ModelAdmin のメソッド

save_model(self, request, obj, form, change)

save_model メソッドは HttpRequest, モデルインスタンス、 ModelForm インスタンス、オブジェクトの追加か変更かを表すブール値 (変更 の場合には True) を引数にとります。このメソッドを使えば、オブジェクトの 保存前 (pre-save) および保存後 (post-save) 処理を実行できます。

保存前に request.user をオブジェクトに保存するには、以下のようにします:

class ArticleAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.user = request.user
        obj.save()

save_formset(self, request, form, formset, change)

save_formset メソッドは HttpRequest, モデルインスタンス、親クラスの ModelForm インスタンス、オブジェクトの追加か変更かを表すブール値 (変更 の場合には True) を引数にとります。

フォームセットの各モデルインスタンスの保存前に request.user をオブジェ クトに保存するには、以下のようにします:

class ArticleAdmin(admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for instance in instances:
            instance.user = request.user
            instance.save()
        formset.save_m2m()

ModelAdmin のメディア定義

たまに、 CSS や JavaScript を追加・変更ビューに追加したい場合があります。 ModelAdmin に内部クラス Media を定義すれば、これを実現できます:

class ArticleAdmin(admin.ModelAdmin):
    class Media:
        css = {
            "all": ("my_styles.css",)
        }
        js = ("my_code.js",)

これらのメディア名のパスには MEDIA_URL の内容が前置されます。また、 forms の通常のメディア定義 と同じ規則が当てはま ります。

admin にカスタムのバリデーションを追加する

admin にカスタムのバリデーションを追加するのはとても簡単です。 admin インタ フェースは django.forms を再利用しているので、 ModelAdmin クラス を使えばフォームを指定できます:

class ArticleAdmin(admin.ModelAdmin):
    form = MyArticleAdminForm

MyArticleAdminForm は、 import できる場所ならどこにでも置けます。フォー ムの中で、任意のフィールドにカスタムのバリデーションコードを追加できます:

class MyArticleAdminForm(forms.ModelForm):
    class Meta:
        model = Article

    def clean_name(self):
        # do something that validates your data
        return self.cleaned_data["name"]

このとき、 ModelForm を使うことが重要です。他のクラスではうまくいきませ ん。詳しくは forms ドキュメント内の カスタムのバリデーション を参照してください。

InlineModelAdmin オブジェクト

admin インタフェースには、他のモデルクラスのインスタンスを同じページで編集 する機能があります。この機能をインライン編集と呼びます。インラインモデルを 指定するには、 ModelAdmin.inlines 属性を使います:

class BookInline(admin.TabularInline):
    model = Book

class AuthorAdmin(admin.ModelAdmin):
    inlines = [
        BookInline,
    ]

Django は、 InlineModelAdmin のサブクラスとして、以下の二つを提供してい ます:

* ``TabularInline``
* ``StackedInline``

これら二つの違いは、単にレンダリングに使われるテンプレートに過ぎません。

InlineModelAdmin のオプション

InlineModelAdmin クラスは ModelAdmin のサブクラスなので、以下に挙げ る独自の機能の他に、 ModelAdmin の機能を全て継承しています:

model

インライン編集する対象のモデルです。必須の引数です。

fk_name

モデルの外部キーの名前です。たいていの場合、外部キーは自動的に決定できます が、親モデルに対して外部キーを複数張っているようなモデルの場合は、 fk_name を明示的に決める必要があります。

formset

デフォルト値は BaseInlineFormset です。独自のフォームセットを指定すれば、 色々とカスタマイズできます。インラインオブジェクトのフォームは モデルフォームセット で生成されています。

form

form の値は ModelAdmin から継承されます。この値は、インラインオブジェ クトのフォームセットを生成するときに fomrset_factory に渡されます。

extra

フォームセットが、初期のフォームに加えて表示する追加のフォーム数を制御しま す。詳しくは formsets のドキュメント を参照 してください。

max_num

インラインで表示するフォームの最大個数を制御します。この値は直接オブジェク トの数には関係しませんが、あまり小さな値が設定されていると、影響することも あります。詳しくは 編集可能なオブジェクトの数を制限する を参照してください。

raw_id_fields

デフォルトの設定では、 Django の admin は ForeignKey の選択に選択ボック スインタフェース (<select>) を使います。しかし、リレーション先の全てのイン スタンスをドロップダウンに表示するときのオーバヘッドを防ぎたいときがありま す。

raw_id_fields には、モデルの ForeignKeyManyToManyField をリ ストで指定します。 raw_id_fields に指定したフィールドは、 Input ウィ ジェットを使います:

class BookInline(admin.TabularInline):
    model = Book
    raw_id_fields = ("pages",)

template

インラインオブジェクトをレンダリングするときに使われるテンプレートです。

verbose_name

モデルの内部クラス Metaverbose_name の設定をオーバライドします。

verbose_name_plural

モデルの内部クラス Metaverbose_name_plural の設定をオーバライド します。

一つのモデルから複数の外部キーが張られている場合のインライン編集の扱い

一つのモデルに対して、複数の外部キーが張られている場合があります。以下のよ うなモデルを例に考えてみましょう:

class Friendship(models.Model):
    to_person = models.ForeignKey(Person, related_name="friends")
    from_person = models.ForeignKey(Person, related_name="from_friends")

Person の admin サイトで、インラインの追加・編集ページを表示したくても、 どの外部キーを辿らせるかは自動で決められません。従って、明示的に決めてやる 必要があります:

class FriendshipInline(admin.TabularInline):
    model = Friendship
    fk_name = "to_person"

class PersonAdmin(admin.ModelAdmin):
    inlines = [
        FriendshipInline,
    ]

中間モデルを介した多対多リレーションを扱う

デフォルトの設定では、 admin で多対多のリレーションを表す場合、 ManyToManyField を定義しているモデルの中でインライン表示されるウィジェッ トを使います。しかし、 ManyToManyFieldthrough 引数に中間モデル を指定した場合、 admin は ManyToManyField のウィジェットを表示しません。 これは、中間モデルの各インスタンスを表示するのに一つのウィジェットでは足り ないためと、複数ウィジェットを表示するために必要なレイアウトが中間モデルに よって変わってしまうためです。

しかし、それでもインラインで編集を行いたい場合があります。好運なことに、イ ンライン admin モデル (inline admin model) を使えば簡単に実現できます。 例として、以下のようなモデルを考えましょう:

class Person(models.Model):
    name = models.CharField(max_length=128)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

中間モデルを admin で表示するには、まず Membership モデルに対するインラ インクラスを作成します:

class MembershipInline(admin.TabularInline):
    model = Membership
    extra = 1

上の簡単な例では、デフォルトの Membership モデルを扱うインライン admin クラスを定義して、追加用のフォームの数を 1 つに制限する他はデフォルトの InlineModelAdmin クラスの値を使っています。他にも、 InlineModelAdmin クラスのオプションを使って色々と設定できます。

次に、 PersonGroup モデルの ModelAdmin クラスを定義します:

class PersonAdmin(admin.ModelAdmin):
    inlines = (MembershipInline,)

class GroupAdmin(admin.ModelAdmin):
    inlines = (MembershipInline,)

最後に、 PersonGroup を admin サイトに登録します:

admin.site.register(Person, PersonAdmin)
admin.site.register(Group, GroupAdmin)

これで、 admin サイトの PersonGroup の両方で、 MembershipInline オブジェクトをインライン編集できます。

一般化リレーションをインラインで扱う

一般化リレーションオブジェクトもインラインで扱えます。以下のようなモデルが 定義されていたとしましょう:

class Image(models.Model):
    image = models.ImageField(upload_to="images")
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey("content_type", "object_id")

class Product(models.Model):
    name = models.CharField(max_length=100)

Product の追加/変更ビューで Image インスタンスを編集したり追加した りしたいなら、 django.contrib.contenttypes.genericGenericInlineModelAdmin を使います。 admin.py で、以下のように設定 してください:

from django.contrib import admin
from django.contrib.contenttypes import generic

from myproject.myapp.models import Image, Product

class ImageInline(generic.GenericTabularInline):
    model = Image

class ProductAdmin(admin.ModelAdmin):
    inlines = [
        ImageInline,
    ]

admin.site.register(Product, ProductAdmin)

django.contrib.contenttypes.genericGenericTabularInlineGenericStackedInline を提供していて、その動作は通常のインライン編集と 同じです。詳しくは contenttypes のドキュメント を参照してください。

admin テンプレートのオーバライド

admin モジュールが admin サイトの様々なページを生成するときに使うテンプレー トは、その大半を比較的簡単にオーバライドできます。オーバライドは、特定のア プリケーションやモデルだけのためにもオーバライドできます。

プロジェクトの admin テンプレートディレクトリを設定する

admin テンプレートファイルは contrib/admin/templates/admin ディレクトリ に入っています。

テンプレートをオーバライドするには、まずプロジェクトの templates ディレ クトリ内に admin ディレクトリを作成します。プロジェクトのテンプレートディ レクトリとは、 TEMPLATE_DIRS の中に設定されているディレクトリのいずれか です。

次に、 admin ディレクトリ内に、アプリケーションの名前から取った名前のディ レクトリを作成します。アプリケーションのサブディレクトリの中に、さらにモデ ルからとった名前のサブディレクトリを作成します。このとき、 admin アプリケー ションはモデル名を小文字に変換した名前でディレクトリを探すことに注意しましょ う。ですから、大小文字を区別するファイルシステム上にアプリケーションを配置 しているのなら、ディレクトリ名が全て小文字で構成されているか確認してくださ い。

特定のアプリケーション向けに admin テンプレートをオーバライドしたければ、テ ンプレートを django/contrib/admin/templates/admin からアプリケーション 名のディレクトリ下にコピーして編集してください。

例えば、 my_app の全てのモデルの変更リストにツールメニューを追加したい なら、 contrib/admin/templates/admin/change_list.html をプロジェクトの templates/admin/my_app/ ディレクトリにコピーして必要な変更を施します。

Page という名前のモデルの変更リストだけにツールメニューを追加したいなら、 ファイルを templates/admin/my_app/page ディレクトリにコピーします。

admin テンプレートのオーバライドと置き換え

admin テンプレートはモジュラー構造で設計されているので、必ずしもいつもテン プレート全体を置き換える必要はありませんし、そうしないよう勧めます。基本的 には、変更の必要のあるセクションだけをオーバライドするのがよいでしょう。

上に挙げた例の続きとして説明しましょう。 Page モデルの History ツー ルの隣に新しくリンクを追加したいとします。 change_from.html を眺めたあ と、オーバライドする必要があるのは object-tools ブロックだけだと分かっ たとしましょう。その場合、新たな change_form.html は以下のように書けま す:

{% extends "admin/change_form.html" %}
{% load i18n %}
{% block object-tools %}
{% if change %}{% if not is_popup %}
  <ul class="object-tools">
    <li><a href="history/" class="historylink">{% trans "History" %}</a></li>
    <li><a href="mylink/" class="historylink">My Link</a></li>
    {% if has_absolute_url %}
        <li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">
            {% trans "View on site" %}</a>
        </li>
    {% endif%}
  </ul>
{% endif %}{% endif %}
{% endblock %}

これだけです!このファイルを templates/admin/my_app ディレクトリに置け ば、全てのモデル変更フォームでリンクが表示されるでしょう。

アプリケーションやモデル単位でオーバライドできるテンプレート

contrib/admin/templates/admin のテンプレートの全てが、アプリケーション やモデル単位でオーバライドできるわけではありません。以下のテンプレートはオー バライドできます:

  • change_form.html
  • change_list.html
  • delete_confirmation.html
  • object_history.html

この節で述べた方法でテンプレートをオーバライドできなくても、オーバライドし たい内容を templates/admin ディレクトリに配置することで、プロジェクト全 体の設定としてオーバライドできます。この方法は、カスタムの 404 ページや 500 ページをオーバライドするときに特に便利です。

Note

change_list_request.html のようなテンプレートは、カスタムの inclusion タグをレンダするために使われています。このテンプレートはオー バライドできますが、テンプレートを変更するときには、独自の inclusion タ グを作成しておいて、別の名前を付けておき、テンプレートで呼び出す方が よいでしょう。そうすれば、テンプレートを選択して使えるからです。

ルートテンプレートとログインテンプレート

インデクスページやログインページのテンプレートを変更したければ、 AdminSite インスタンスを自作して、 index_templatelogin_template プロパティの値を変更してください。

AdminSite オブジェクト

Django の管理サイトは django.contrib.admin.sites.AdminSite のインスタン スを使って表現しています。デフォルトでは、このクラスのインスタンスは django.contrib.admin.site としてすでに作成されていて、モデルや ModelAdmin インスタンスを登録できます。

独自のふるまいを実現する管理サイトを作りたければ、 AdminSite のサブクラ スを作成して、 (他の Python クラスと同様に) 必要な機能をオーバライドし、 作成したインスタンスにモデルや ModelAdmin サブクラスを登録してください。

URLconf に AdminSite インスタンスをフックする

Django admin サイト構築の最後のステップは、 AdminSite インスタンスの URLconf への設置です。設置を行うには、 URL を AdminSite.root メソッドに 振り向けます。

以下の例では、デフォルトの AdminSite インスタンスである django.contrib.admin.site を、 /admin/ という URL にマップしていま す:

# urls.py
from django.conf.urls.defaults import *
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
    ('^admin/(.*)', admin.site.root),
)

この例では、 admin.autodiscover() を使って、 INSTALLED_APPSadmin.py モジュールを自動的にロードしています。

次の例では、 AdminSite インスタンス myproject.admin.admin_site/myadmin/ という URL にマップしています:

# urls.py
from django.conf.urls.defaults import *
from myproject.admin import admin_site

urlpatterns = patterns('',
    ('^myadmin/(.*)', admin_site.root),
)

このように、 AdminSite インスタンスを自作している場合は、 myproject.admin モジュールの中で各アプリケーションの admin.py モジュー ルを import しているはずなので、 autodiscover を呼ぶ必要はありません。

URLパターンの正規表現には、 admin サイトのルート URL の後ろ全ての URL 部分 を含むようなグループ表記を 必ず 入れておかねばなりません。この例では (.*) を使っています。

一つの URLconf で複数の admin サイトを使う

Django で作られた一つの Web サイト上で、複数の admin サイトインスタンスを簡 単に運営できます。方法は、単に AdminSite インスタンスを複数にして別々の URL で設置するだけです。

下の例では、 /basic-admin//advanced-admin/ という二つの URL で、 myproject.admin.basic_sitemyproject.admin.advanced_site という AdminSite インスタンスを使って、別々のバージョンの admin サイトを運営し ています:

# urls.py
from django.conf.urls.defaults import *
from myproject.admin import basic_site, advanced_site

urlpatterns = patterns('',
    ('^basic-admin/(.*)', basic_site.root),
    ('^advanced-admin/(.*)', advanced_site.root),
)