Django:从管理员的form.clean()访问请求对象

问题描述 投票:0回答:4

我的问题与此非常相似:如何访问请求对象或表单的 clean() 方法中的任何其他变量?

除了,我的管理表单也有同样的问题。所以我看不到自己初始化表单的方法,因此 - 将请求传递给它。

先谢谢了。

django django-admin django-forms
4个回答
50
投票

确实有办法解决你的问题!

您需要对ModelAdmin.get_form()提供的

子类化表单
并覆盖它:

class BusinessDocumentCommentForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        # Voila, now you can access request anywhere in your form methods by using self.request!
        super(BusinessDocumentCommentForm, self).__init__(*args, **kwargs)
        if self.request.GET.get('document_pk', False):
            #Do something
    def clean(self):
        # Do something with self.request
        # etc.    
    class Meta:
        model = BusinessDocumentComment

class BusinessDocumentCommentAdmin(admin.ModelAdmin):

    form = BusinessDocumentCommentForm     

    def get_form(self, request, obj=None, **kwargs):

        AdminForm = super(BusinessDocumentCommentAdmin, self).get_form(request, obj, **kwargs)

        class AdminFormWithRequest(AdminForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return AdminForm(*args, **kwargs)

        return AdminFormWithRequest

2
投票

这个解决方案对我有用。您可以在表单中的任何位置使用

self.request
来使用它,包括
def clean(self)

class MyModelAdmin(admin.ModelAdmin):
    form = MyForm

    def get_form(self, request, *args, **kwargs):
        form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
        form.request = request
        return form

1
投票

ModelAdmin
类中有许多挂钩可以让您执行此操作 - 请查看
django.contrib.admin.options
中的代码。

可能对您有帮助的两个方法是

ModelAdmin.save_form
ModelAdmin.save_model
,这两个方法都传递请求对象。因此,您可以在 Admin 子类中重写这些方法并执行您需要的任何额外处理。

评论后已编辑

您说得很对,这不会让您根据用户的权限验证表单。不幸的是,表单实例化深深地埋藏在

add_view
change_view
ModelAdmin
方法中。

如果不复制大量现有代码,就没有多少可能性。您可以重写

*_view
方法;或者您可以尝试重写
modelform_factory
函数以返回一个已嵌入请求对象的新类;或者您可以尝试摆弄表单类
__new__
方法来执行相同的操作,但由于表单元类,这很棘手。


0
投票

使用此函数作为表单类的代理,允许我们在表单实例上注入请求,以便我们可以在干净的方法中访问它。

通过使用如下解决方案(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

总体而言,最佳实践是将特定于请求的数据保留在请求范围内,并避免将其直接存储在表单类或其他共享对象中,以保持适当的隔离并防止潜在问题。

© www.soinside.com 2019 - 2024. All rights reserved.