フォームにアクセスするユーザの権限に応じて、フォーム中のフィールドの編集可否を切り替えるつもりです
一応、表示フィールドの生成に併せて 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/
> 管理サイトのインラインフォームで、モデルフィールドから生成される
> 表示フィールドとは別の表示フィールドを作る
実現したいのは、こういうことでしょうか?:
================= 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 さんは書きました:
ほぼそういうことなのですが、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 を継承したクラス) を呼び出すようにします
> また、ご提示の例のように 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 さんは書きました:
ありがとうございます
何か勘違いをしていたようです
使っている Django のバージョンは 1.0 です
ご提示のコードを基に実際に試したところ、期待通りの動作をしました
やっぱり、form メンバ変数を変えた ModelAdminサブクラスや TabluerInlineを複数用意するのが簡単そうです