在Django的
ModelAdmin
中,我需要显示根据用户拥有的权限定制的表单。有没有办法将当前用户对象放入表单类中,以便我可以在其 __init__
方法中自定义表单?
我认为将当前请求保存在本地线程中是可能的,但这将是我的最后手段,因为我认为这是一种糟糕的设计方法。
这是我最近为博客所做的事情:
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, *args, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user
return form
我现在可以通过访问
forms.ModelForm
来访问我的
self.current_user
中的当前用户
Joshmaker 的答案在 Django 1.7 上对我不起作用。这是我必须为 Django 1.7 做的事情:
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, obj=None, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, obj, **kwargs)
form.current_user = request.user
return form
有关此方法的更多详细信息,请参阅相关的 Django 文档
此用例记录在 ModelAdmin.get_form
[...]如果您想向超级用户提供其他字段,您可以交换不同的基本形式,如下所示:
class MyModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if request.user.is_superuser:
kwargs['form'] = MySuperuserForm
return super().get_form(request, obj, **kwargs)
如果你只需要保存一个字段,那么你可以覆盖 ModelAdmin.save_model
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super().save_model(request, obj, form, change)
我想我找到了一个适合我的解决方案:要创建一个
ModelForm
Django 使用管理员的 formfield_for_db_field
方法作为回调。 def formfield_for_dbfield(self, db_field, **kwargs):
field = super(MyAdmin, self).formfield_for_dbfield(db_field, **kwargs)
field.user = kwargs.get('request', None).user
return field
现在我可以通过以下形式访问当前用户对象:
__init__
:
current_user=self.fields['fieldname'].user
偶然发现了同样的事情,这是我页面上的第一个谷歌结果。Dint 有所帮助,更多的谷歌搜索并成功了!!
这对我来说是如何工作的(django 1.7+):
class SomeAdmin(admin.ModelAdmin):
# This is important to have because this provides the
# "request" object to "clean" method
def get_form(self, request, obj=None, **kwargs):
form = super(SomeAdmin, self).get_form(request, obj=obj, **kwargs)
form.request = request
return form
class SomeAdminForm(forms.ModelForm):
class Meta(object):
model = SomeModel
fields = ["A", "B"]
def clean(self):
cleaned_data = super(SomeAdminForm, self).clean()
logged_in_email = self.request.user.email #voila
if logged_in_email in ['[email protected]']:
raise ValidationError("Please behave, you are not authorised.....Thank you!!")
return cleaned_data
解决此问题的另一种方法是使用 Django currying,这比仅将请求对象附加到表单模型要干净一些。
from django.utils.functional import curry
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, **kwargs)
return curry(form, current_user=request.user)
这具有额外的好处,使表单上的 init 方法更加清晰,因为其他人会理解它是作为 kwarg 传递的,而不仅仅是在初始化之前将属性随机附加到类对象。
class BlogPostForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.current_user = kwargs.pop('current_user')
super(BlogPostForm, self).__init__(*args, **kwargs)
使用此函数作为表单类的代理,允许我们在表单实例上注入请求,以便我们可以在干净的方法中访问它。
通过使用如下解决方案(get_form_class_with_request),您可以将请求注入隔离到表单实例创建的时刻。这确保了每个表单实例都有自己的请求数据,从而降低了共享状态导致的冲突和意外行为的风险。
def get_form_class_with_request(
form_class: type[forms.ModelForm], request: HttpRequest
) -> Callable[[tuple[Any, ...], dict[str, Any]], ModelForm]:
def init_form(*args, **kwargs) -> ModelForm:
form_instance = form_class(*args, **kwargs)
form_instance.request = request
form_instance.current_user = request.user
return form_instance
init_form.base_fields = form_class.base_fields
return init_form
class CustomModelAdmin(admin.ModelAdmin):
form = YourForm
def get_form(self, request, *args, **kwargs):
form = super().get_form(request, *args, **kwargs)
form_proxy = get_form_class_with_request(form, request)
return form_proxy
总体而言,最佳实践是将特定于请求的数据保留在请求范围内,并避免将其直接存储在表单类或其他共享对象中,以保持适当的隔离并防止潜在问题。