在Django管理相关的内联的验证(Validation of dependant inlines

2019-07-04 02:20发布

我使用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)的总和。

我试过了:

  • 在Shopping.clean方法提高ValidationError。 但行内都还没有更新在干净故其总和是不正确的
  • 在ShoppingAdmin.save_related方法提高ValidationError。 但提高ValidationError这里给出了一个非常不友好的用户错误页面,而不是重定向到更改页一个不错的错误消息。

有没有办法解决这个问题? 是客户端(JavaScript / AJAX)验证的最简单?

Answer 1:

你可以重写你的内联表单集来实现你想要的。 在表单集的清洁方法,你必须通过“实例”成员访问到您的购物实例。 因此,你可以使用的购物模式,临时存储计算的总,让你的表单集通信。 在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

这是唯一的清洁方式,你可以做到这一点(据我所知),一切都放置在它应该是。

编辑:增加了*如果form.cleaned_data *检查,因为表单包含空的内联为好。 请让我知道这对你的作品!

EDIT2:增加了对即将被删除形式的检查,如正确的评论中指出。 这些形式不应参与计算。



Answer 2:

好吧,我有一个解决方案。 它涉及编辑admin界面的代码。

在Django /了contrib /管理/ 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的功能要求?



文章来源: Validation of dependant inlines in django admin