Django管理员 - 在BaseInlineFormSet中访问request.user

4

我刚刚创建了一个forms.models.BaseInlineFormSet以覆盖TabularInline模型的默认表单集。在表单集验证(clean)过程中,我需要评估用户组,因为一些组必须在(0,20)的范围内写入一个数字。

我正在使用django管理工具来自动生成界面。

我尝试从init方法的kwargs中获取请求和用户,但是我无法得到引用。

这是我现在拥有的:

class OrderInlineFormset(forms.models.BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')
        super(OrderInlineFormset, self).__init__(*args, **kwargs)

    def clean(self):
        # get forms that actually have valid data
        count = 0
        for form in self.forms:
            try:
                if form.cleaned_data:
                    count += 1
                    if self.user.groups.filter(name='Seller').count() == 1:
                        if form.cleaned_data['discount'] > 20:
                            raise forms.ValidationError('Not authorized to specify a discount greater than 20%')
            except AttributeError:
                # annoyingly, if a subform is invalid Django explicity raises
                # an AttributeError for cleaned_data
                pass
        if count < 1:
            raise forms.ValidationError('You need to specify at least one item')

class OrderItemInline(admin.TabularInline):
    model = OrderItem
    formset = OrderInlineFormset

然后我在我的ModelAdmin中使用inlines = [OrderItemInline,]

不幸的是,self.user总是None,所以我无法比较用户组并且筛选器没有被应用。我需要进行筛选,因为其他组应该能够指定任何折扣百分比。

我该怎么办?如果您还需要ModelAdmin代码,我会发布它的(我只是避免复制整个代码以避免混淆)。

2个回答

7

我在你的问题中看到了我的代码,所以我想我最好还是尝试回答一下。但首先我要说的是,那个片段实际上只是用来验证表单集中最少数量的表单。你的用例不同 - 你想检查每个表单中的某些内容。这应该通过表单级别的验证来完成,而不是表单集。

话虽如此,问题实际上并不在于你发布的代码,而在于它只是其中的一部分。显然,如果你想在表单或表单集初始化时从kwargs中获取用户,你需要确保用户实际上被传递到初始化中 - 默认情况下并没有这样做。

不幸的是,Django的管理界面并没有真正给你一个合适的钩子来拦截初始化本身。但你可以通过覆盖get_form函数并使用functools.partial将表单类与请求参数包装起来来欺骗系统(这段代码经过了相当少量的测试,但应该可以工作):

from functools import partial

class OrderForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')
        super(OrderForm, self).__init__(*args, **kwargs)

    def clean(self)
         if self.user.groups.filter(name='Seller').count() == 1:
             if self.cleaned_data['discount'] > 20:
                 raise forms.ValidationError('Not authorized to specify a discount greater than 20%')
         return self.cleaned_data

class MyAdmin(admin.ModelAdmin):
    form = OrderForm

    def get_form(self, request, obj=None, **kwargs):
        form_class = super(MyAdmin, self).get_form(request, obj, **kwargs)
        return functools.partial(form_class, user=request.user)

3
很抱歉挖出这么旧的帖子,但我偶然发现了这个解决方案,实施时遇到了问题:'functools.partial'对象没有属性'base_fields'。 - Matthew
@Matthew,您不能再使用部分技巧来处理新版本的Django了,因为它假定get_form的结果是一个类(具有base_fields)。 - sherpya

2

这里有另一种不使用部分视图的选项。首先,在您的TabularInline类中覆盖get_formset方法。

像下面的示例一样将request.user或任何其他需要在表单集中可用的额外变量分配:

class OrderItemInline(admin.TabularInline):
    model = OrderItem
    formset = OrderInlineFormset

    def get_formset(self, request, obj=None, **kwargs):  
       formset = super(OrderProductsInline, self).get_formset(request, obj, **kwargs)
       formset.user = request.user
       return formset

现在用户以 self.user 的形式出现在表单集中。
class OrderInlineFormset(forms.models.BaseInlineFormSet):

    def clean(self):
      print(self.user) # is available here 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接