管理サイトの表示フィールドのカスタマイズ

399 views
Skip to first unread message

Nakane Ryuji

unread,
Nov 16, 2008, 2:30:58 AM11/16/08
to djan...@googlegroups.com
お手軽にサイトを立ち上げようと django.contrib.admin (管理サイト) のフォームを元に、表示をカスタマイズしています

フォームにアクセスするユーザの権限に応じて、フォーム中のフィールドの編集可否を切り替えるつもりです
一応、表示フィールドの生成に併せて widget を差し替えることで何とかなりそうな感触がつかめました

管理サイトでは、django.contrib.admin.ModelAdmin の formfield_for_dbfield メソッドで
db_filed.formfield() を呼び出して表示用フィールドを生成しているようです
ただし、formfield_for_dbfield メソッドの中で db_filed.formfield()
を呼び出す前に、管理サイト独自に定義した widget を使うように強制しています
このため、フォームフィールドに django.forms.PasswordField を使うようにしていても、管理サイトでは必ず
django.cnorib.admin.widgets.AdminTextInput ウィジェット
(django.forms.TextInput とほぼ同じウィジェット) が強制されてしまいます
そこで、ModelAdmin を継承したクラスで formfield_for_dbfield メソッドをオーバーライドして、素直に
return db_filed.formfield(widget=mywidget)
してしまうことで管理サイト独自のウィジェットを使わせないようにしました

このように、モデルに直接紐付くフォームの表示フィールドを自由に切り替えることができたのですが、インラインフォームの場合は未だ解決できません
django.contrib.admin.StackedInline や
django.contrib.admin.TabularInline にも formfield_for_dbfield
メソッドはありますが、フォームの作成の際は使われず、直接モデルフィールドからフォームフィールドを作成しているように思われます

管理サイトのインラインフォームで、モデルフィールドから生成される表示フィールドとは別の表示フィールドを作るには、どのような方法が考えられるかお教えください

# addフォームと changeフォームだけは、管理サイトから離れて自前で全部作る方が簡単なのかなぁ

--
Nakane Ryuji living at Nagoya
// mailto:ryu...@gmail.com
// business http://www.compnet.jp/

Yasushi Masuda

unread,
Nov 17, 2008, 11:08:53 PM11/17/08
to djan...@googlegroups.com
Nakane さん

> 管理サイトのインラインフォームで、モデルフィールドから生成される
> 表示フィールドとは別の表示フィールドを作る

実現したいのは、こういうことでしょうか?:

================= models.py ===================
# coding: utf-8
from django.db import models

class Foo(models.Model):
"""インラインフィールドを表示する側のモデル"""
field_a = models.CharField(max_length=100)

class Bar(models.Model):
"""Foo の中でインライン編集されるモデル"""
foo = models.ForeignKey(Foo)
field_b = models.CharField(max_length=100)
field_c = models.CharField(max_length=100)

================= admin.py ====================
# coding: utf-8
from django import forms
from django.contrib import admin
from models import *

class FooForm(forms.ModelForm):
"""Foo のカスタム admin フォーム。"""
field_a = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = Foo

class BarInlineForm(forms.ModelForm):
"""Bar のカスタム admin インライン編集フォーム。"""
field_b = forms.CharField(widget=forms.Textarea)
field_c = forms.CharField(widget=forms.Textarea)
class Meta:
model = Bar

class BarInlineAdmin(admin.TabularInline):
"""Bar のカスタムインライン admin クラス。"""
model = Bar
form = BarInlineForm

class FooAdmin(admin.ModelAdmin):
"""Foo のカスタム admin クラス。"""
form = FooForm
inlines = [BarInlineAdmin, ]

# Foo のカスタム admin を登録。
admin.site.register(Foo, FooAdmin)

================================================

---
Yasushi Masuda
http://ymasuda.jp/

Nakane Ryuji さんは書きました:

Nakane Ryuji

unread,
Nov 18, 2008, 3:09:44 AM11/18/08
to djan...@googlegroups.com
回答ありがとうございます

ほぼそういうことなのですが、ModelAdmin.root()
を呼び出した後に、編集されるオブジェクトによって、フォームを変えたいと考えたために、ModelAdmin
を継承したクラスのカスタマイズを検討していました

また、ご提示の例のように forms.ChareField(wdget=forms.PasswordInput) としても、強制的に
foms.TextInput (正確には AdminTextInputWidget) にされるため、ModelAdmin の継承したクラスで
formfield_for_dbfield メソッドをオーバーライドする必要があります
インラインフォームは強制的に widget を差し替えるようなことももなく、指定した widget をそのまま使って表示するようです

編集されるオブジェクトに応じて、動的に使用する widget を差し替える目的であったため、ModelAdmin
を継承したクラスをカスタマイズして、クラスの内部に隠蔽しようとの考えでした
親フォームは上記のように、formfield_for_dbfield メソッドをオーバーライドすれば目的を達成できました
しかしよくよく調べた結果、親フォームのクラス (ModelAdmin を継承した inlines メンバ変数を持つクラス)
の初期化時に、インラインフォームのインスタンスが生成されてしまうため、その後にインラインフォームを widget
を差し替えることは困難だとわかってしまいました

仕方ないので、ModelAdmin を継承したクラスを呼び出す前に、編集対象のオブジェクトに応じて、それぞれ別のクラス
(ModelAdmin を継承したクラス) を呼び出すようにします

Yasushi Masuda

unread,
Nov 18, 2008, 4:42:54 AM11/18/08
to djan...@googlegroups.com
Nakaneさん

> また、ご提示の例のように forms.ChareField(wdget=forms.PasswordInput) としても、強制的に
> foms.TextInput (正確には AdminTextInputWidget) にされるため、ModelAdmin の継承したクラスで
> formfield_for_dbfield メソッドをオーバーライドする必要があります

1.0からそう離れていない trunk を使っている私の環境では、Foo のフィールドは
(モデルから取り出した場合の通常のTextInputではなく)PasswordInputで表示されて
います。どのバージョンの Django をお使いですか?私の例では

* モデルFoo, Barでは models.CharField を使う。
- このまま ModelForm を使うと field_a は TextInput.
- このまま admin で表示すると field_a は TextInput.
* Foo の admin をカスタマイズするために FooForm を作って、 FooAdmin の form に指定する。
- Foo の ModelForm を使うと field_a は TextInput.
- Foo を admin で表示すると field_a は PasswordTextInput.

を実現するよう書いたのですが、そういう動作を目指しているのではないんで
しょうか。

> 編集されるオブジェクトに応じて、動的に使用する widget を差し替える目的であったため、ModelAdmin

リクエストの内容に応じて admin を切り替えたければ、
django.contrib.admin.sites.AdminSite のインスタンスを複数生成して、動的に
AdminSite.root を選択すればよいと思います。

============ models.py =================
# モデル定義
class MyModel(models.Model):
field_a = models.CharField(...)
field_b = models.CharField(...)

============ admin.py ==================
# TextInput の admin フォーム
class TextInputForm(forms.ModelForm):
class Meta:
model = MyModel

# PasswordInput の admin フォーム
class PasswordInputForm(forms.ModelForm):
field_a = forms.CharField(widget=forms.PasswordInput)
field_b = froms.CharField(widget=forms.PasswordInput)
class Meta:
model = MyModel

# TextInput フォームを使った ModelAdmin
class TextManiacAdmin(admin.ModelAdmin):
form = TextInputForm

# PasswordInput フォームを使った ModelAdmin
class PasswordManiacAdmin(admin.ModelAdmin):
form = PasswordInputForm

from django.contrib.admin import AdminSite
# MyModel に対して TextManiacAdmin を表示する admin
text_maniac_site = AdminSite(); text_maniac_site.register(MyModel, TextManiacAdmin)
# MyModel に対して PasswordManiacAdmin を表示する admin
password_maniac_site = AdminSite(); password_maniac_site.register(MyModel, PasswordManiacAdmin)

============== views.py ==================
from admin import text_maniac_site, password_maniac_site
...
def selective_admin(request, url):
...
if selector_function(request, url)=='HE_IS_TEXT_MANIAC':
callback = text_maniac_site.root(request, url)
elif selector_function(request, url)=='HE_IS_PASSWORD_MANIAC':
callback = password_maniac_site.root(request, url)
else:
raise Http404
return callback(request, url)

============== urls.py ===============

pattern = urlpattern('',
(r'^admin/(.*)', views.selective_admin),
...)


---
Yasushi Masuda
http://ymasuda.jp/

Nakane Ryuji さんは書きました:

Nakane Ryuji

unread,
Nov 20, 2008, 9:09:06 PM11/20/08
to djan...@googlegroups.com
Masudaさん

ありがとうございます
何か勘違いをしていたようです

使っている Django のバージョンは 1.0 です
ご提示のコードを基に実際に試したところ、期待通りの動作をしました

やっぱり、form メンバ変数を変えた ModelAdminサブクラスや TabluerInlineを複数用意するのが簡単そうです

Reply all
Reply to author
Forward
0 new messages