maru さん
将来のバージョンで期待通りに動作する保証はないのですが、
1.0 で近いことを実現するハックが、あることにはあります。
テンプレートの編集
=====================
バージョン 1.0 以前から、 Django には管理画面のテンプレートをカスタマイズ
する方法がありました。たとえば、 adressbook というアプリケーションの
Entry というモデルの管理画面のレコード一覧(「チェンジリスト」といいます)
の表示をいじりたければ、 "admin/addressbook/entry/change_list.html" という
テンプレートを、アプリケーションのテンプレートディレクトリ下
(addressbook/templates/admin/addressbook/entry/change_list.html) に
作成します。デフォルトの change_list.html の内容は、 django のインストール
ディレクトリの django/contrib/admin/templates/admin/change_list.html
です。継承を使えば、全部をコピーして書き換えなくても、ツールバーの
検索部分だけをオーバライドできます。
オーバライドするべきブロックは、search です。
元のテンプレートでは、以下のようになっているでしょう::
...
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
{% block search %}{% search_form cl %}{% endblock %} ←ここです。
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
...
search ブロックの中に入っている {% search_form cl %} は
django.contrib.admin.templatetags.admin_list で定義されているインクルージョン
タグです。この機能をちょっといじりたいので、 change_list.html テンプレートには
以下のように記述します::
{# -*- coding: utf-8 -*- #}
{% extends "admin/change_list.html" %}
{% load my_tags %}
{% block search %}{% my_search_form cl %}{% endblock %}
こうしておくと、search_form テンプレートタグが呼出される代りに、
(あとで説明する)自作の my_search_form テンプレートタグが呼び出されて、
複数のフィールドを出力するようカスタマイズしたテンプレートでレンダリング
されるようになります。 {% load my_tags %} では、自作のテンプレートタグ
ライブラリをロードしていますが、このライブラリは後で作ります。
さて、search_form タグは、
django/contrib/admin/templates/admin/search_form.html
というテンプレートを使ってレンダリングを行うので、このファイルを
addressbook/templates/admin/addressbook/entry/my_search_form.html
にコピーして、以下のように書き換えておきます::
********************** ここから ***************************
{# -*- coding: utf-8 -*- #}
{% load adminmedia %}
{% load i18n %}
{% if cl.search_fields %}
<div id="toolbar">
<form id="changelist-search" action="" method="get">
<div><!-- DIV needed for valid HTML -->
{# 元のテンプレートでは、ここに検索フィールドが入っている #}
<label for="searchbar">ID:</label>{# 変更箇所 #}
<input type="text" size="20" name="id"
value="{{
cl.params.id }}" id="searchbar" />{# ID #}{# 変更箇所 #}
<label for="searchbar">NAME:</label>{# 変更箇所 #}
<input type="text" size="20" name="name"
value="{{
cl.params.name }}" id="searchbar" />{# NAME #}{# 変更箇所 #}
<input type="submit" value="{% trans 'Go' %}" />{# 変更箇所 #}
<label for="searchbar">TEL:</label>{# 変更箇所 #}
<input type="text" size="20" name="tel"
value="{{
cl.params.tel }}" id="searchbar" />{# TEL #}{# 変更箇所 #}
<input type="submit" value="{% trans 'Go' %}" />
{% if show_result_count %}
<span class="small quiet">
{% blocktrans count cl.result_count as counter %}
1 result{% plural %}{{ counter }} results
{% endblocktrans %}
(<a href="?{% if cl.is_popup %}pop=1{% endif %}">
{% blocktrans with cl.full_result_count as full_result_count %}
{{ full_result_count }}
total{% endblocktrans %}</a>)
</span>
{% endif %}
{% for pair in cl.params.items %}
{% ifnotequal pair.0 "id" %}{# 変更箇所 #}
{% ifnotequal pair.0 "name" %}{# 変更箇所 #}
{% ifnotequal pair.0 "tel" %}{# 変更箇所 #}
<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}"/>
{% endifnotequal %}{# 変更箇所 #}
{% endifnotequal %}{# 変更箇所 #}
{% endifnotequal %}{# 変更箇所 #}
{% endfor %}
</div>
</form>
</div>
<script type="text/javascript">
document.getElementById("searchbar").focus();
</script>
{% endif %}
********************** ここまで ***************************
ポイントは、検索フィールドを増やすときに、<input> の name 属性を
検索したいフィールド名にすることと、 対応する value 属性を、
{{ cl.params.(フィールド名) }} にすること、そして、後半の
{% for pair in cl.params.items %} の中で、 pair.0 と検索フィールド
名が一致するときに
<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}"/>
が実行されないよう ifnotequal をかぶせておくことです。
テンプレートタグを定義する
============================
ここまできたら、今度は
次に、 {% my_search_form cl %} を実行するテンプレートタグを定義します。
テンプレートタグのライブラリを作るには、 addressbook/templatetags/
ディレクトリを作り、その下に __init__.py , my_tags.py という名前の
二つのファイルを作成します。 __init__.py の内容は空で、 my_tags.py
は以下のように記述します::
from django.template import Library
from django.contrib.admin.templatetags.admin_list import search_form
register = Library()
@register.inclusion_tag('admin/log/log/my_search_form.html')
def my_search_form(cl):
return search_form(cl)
これで、 search_form の機能はそのままに、テンプレートだけ
my_search_form.html を使うようなインクルージョンタグ my_sarch_form
が my_tags テンプレートライブラリに登録されます。
ModelAdmin をカスタマイズする
===============================
最後に、 ModelAdmin をカスタマイズします。 ModelAdmin は、
addressbook/admin.py で編集します::
from django.contrib import admin
from django.http import QueryDict
from models import Entry
class EntryAdmin(admin.ModelAdmin):
search_fields = ['id', 'name', 'tel']
def __call__(self, request, url):
get_dict = request.GET.copy()
for sf in self.search_fields:
if sf in get_dict and bool(get_dict.get(sf))==False:
get_dict.pop(sf)
request.GET = get_dict; request.GET._mutable = False
return super(EntryAdmin, self).__call__(request, url)
admin.site.register(Entry, EntryAdmin)
ModelAdmin は、管理画面の中で、ビューのように使われます。すなわち、
管理画面を表示するときに、 request, url を引数にして呼び出されます。
上のコードでは、 __call__ を拡張して、 search_fields と同じキーで、
値が空文字列のパラメタが request.GET に入っていたら、 pop して取り
出しています。これは、検索フィールドに値が入っていないときに、
検索条件が「name__exact=''」のようにになってしまうのを防ぐためです。
これで完成です。
補足
=======
同じようなことを実現するのに色々試していて、 name だけでなく、
name__contains のような表現もできるようだと分かりましたが、正しく
動作する保証はありません。もちろん、上の内容も、将来のバージョン
にわたって正しく動作する保証はありません。
---------------
Yasushi Masuda
http://ymasuda.jp/
maru さんは書きました: