我正在使用Django 1.4,我想设置验证规则来比较不同内联的值。
我有三个简单的课程
在models.py中:
class Shopping(models.Model):
shop_name = models.CharField(max_length=200)
class Item(models.Model):
item_name = models.CharField(max_length=200)
cost = models.IntegerField()
item_shop = models.ForeignKey(Shopping)
class Buyer(models.Model):
buyer_name = models.CharField(max_length=200)
amount = models.IntegerField()
buyer_shop = models.ForeignKey(Shopping)
在admin.py中:
class ItemInline(admin.TabularInline):
model = Item
class BuyerInline(admin.TabularInline):
model = Buyer
class ShoppingAdmin(admin.ModelAdmin):
inlines = (ItemInline, BuyerInline)
因此,例如,可以以10美元购买一瓶朗姆酒,以8美元购买伏特加酒。迈克支付15美元,汤姆支付3美元。
目标是防止用户使用不匹配的金额保存实例:已支付的金额必须与项目成本的总和相同(即10 + 8 = 15 + 3)。
我试过了:
这个问题有解决办法吗?客户端(javascript / ajax)验证最简单吗?
答案 0 :(得分:26)
您可以覆盖Inline formset以实现您想要的效果。在formset的clean方法中,您可以通过“instance”成员访问Shopping实例。因此,您可以使用Shopping模型临时存储计算的总计,并使您的表单集进行通信。在models.py中:
class Shopping(models.Model):
shop_name = models.CharField(max_length=200)
def __init__(self, *args, **kwargs)
super(Shopping, self).__init__(*args, **kwargs)
self.__total__ = None
在admin.py中:
from django.forms.models import BaseInlineFormSet
class ItemInlineFormSet(BaseInlineFormSet):
def clean(self):
super(ItemInlineFormSet, self).clean()
total = 0
for form in self.forms:
if not form.is_valid():
return #other errors exist, so don't bother
if form.cleaned_data and not form.cleaned_data.get('DELETE'):
total += form.cleaned_data['cost']
self.instance.__total__ = total
class BuyerInlineFormSet(BaseInlineFormSet):
def clean(self):
super(BuyerInlineFormSet, self).clean()
total = 0
for form in self.forms:
if not form.is_valid():
return #other errors exist, so don't bother
if form.cleaned_data and not form.cleaned_data.get('DELETE'):
total += form.cleaned_data['cost']
#compare only if Item inline forms were clean as well
if self.instance.__total__ is not None and self.instance.__total__ != total:
raise ValidationError('Oops!')
class ItemInline(admin.TabularInline):
model = Item
formset = ItemInlineFormSet
class BuyerInline(admin.TabularInline):
model = Buyer
formset = BuyerInlineFormSet
这是你能做到的唯一干净方式(据我所知),一切都放在应有的位置。
编辑:添加* if form.cleaned_data *检查,因为表单也包含空内联。 请告诉我这对您有何帮助!
EDIT2:添加了对要删除的表单的检查,正如评论中正确指出的那样。这些表格不应参与计算。
答案 1 :(得分:-2)
好吧我有一个解决方案。它涉及编辑django管理员的代码。
在django / contrib / admin / options.py中,在add_view(第924行)和change_view(第1012行)方法中,找到这部分:
[...]
if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, True)
[...]
并将其替换为
if not hasattr(self, 'clean_formsets') or self.clean_formsets(form, formsets):
if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, True)
现在在你的ModelAdmin中,你可以做这样的事情
class ShoppingAdmin(admin.ModelAdmin):
inlines = (ItemInline, BuyerInline)
def clean_formsets(self, form, formsets):
items_total = 0
buyers_total = 0
for formset in formsets:
if formset.is_valid():
if issubclass(formset.model, Item):
items_total += formset.cleaned_data[0]['cost']
if issubclass(formset.model, Buyer):
buyers_total += formset.cleaned_data[0]['amount']
if items_total != buyers_total:
# This is the most ugly part :(
if not form._errors.has_key(forms.forms.NON_FIELD_ERRORS):
form._errors[forms.forms.NON_FIELD_ERRORS] = []
form._errors[forms.forms.NON_FIELD_ERRORS].append('The totals don\'t match!')
return False
return True
这更像是一个黑客而不是一个正确的解决方案。任何改进建议?有人认为这应该是关于django的功能请求吗?