我刚刚创建了一个forms.models.BaseInlineFormSet
来覆盖TabularInline模型的默认formset。我需要在formset验证(clean
)中评估用户的组,因为某些组必须在范围(0,20)内写一个数字。
我正在使用django admin自动生成界面。
我尝试在 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代码,我将发布它(我只是避免复制整个代码以避免混淆)。
答案 0 :(得分:7)
嗯,我在你的问题中认识到我的代码,所以我想我最好试着回答它。但我首先要说的是,该片段实际上仅用于验证formset中的最小数量的表单。您的用例不同 - 您想要检查每个表单中的内容。这应该在表单级别进行验证,而不是表单。
那就是说,问题实际上并不在于您发布的代码,而是因为这只是其中的一部分。显然,如果你想在初始化表单或表单集时从kwargs中获取用户,你需要确保用户实际上已经被传递到初始化 - 默认情况下它不是。
不幸的是,Django的管理员并没有给你一个适当的钩子来拦截初始化本身。但是你可以通过覆盖get_form
函数并使用functools.partial
来包装带有request参数的表单类来作弊(这段代码是合理的未经测试,但应该有效):
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)
答案 1 :(得分:0)
这是不使用局部变量的另一种选择。首先,在您的get_formset
类中覆盖TabularInline
方法。
分配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