在django管理员中验证依赖内联 [英] Validation of dependant inlines in django admin

查看:149
本文介绍了在django管理员中验证依赖内联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Django 1.4,我想设置比较不同内容的验证规则。



我有三个简单的类



在models.py:

  class Shopping(models.Model):
shop_name = models.CharField(max_length = 200)

类项目(models.Model):
item_name = models.CharField(max_length = 200)
cost = models.IntegerField()
item_shop = models.ForeignKey(购物)

类买家(models.Model):
buyer_name = models.CharField(max_length = 200)
amount = models.IntegerField ()
buyer_shop = models.ForeignKey(购物)

在admin.py:

  class ItemInline(admin.TabularInline):
model = Item

class BuyerInline(admin。 TabularInline):
model = Buyer

class ShoppingAdmin(admin.ModelAdmin):
inlines =(ItemInline,BuyerInline)
pre>

所以对于exa可以买到一瓶10美元的价格,一杯伏特加酒8美元。迈克支付15美元,汤姆支付3美元。



目标是防止用户使用不匹配的金额保存实例:已支付的费用必须与项目成本(即10 + 8 = 15 + 3)的总和相同。



我试过:




  • 在Shopping.clean方法中提高ValidationError。但是内联没有更新,因此总和不正确

  • 在ShoppingAdmin.save_related方法中提升ValidationError。但是提高ValidationError这里给出了一个非常用户不友好的错误页面,而不是重定向到更改页面一个很好的错误消息。



有没有解决这个问题?客户端(javascript / ajax)验证最简单?

解决方案

您可以覆盖内联表单以实现您想要的。在表单集的干净方法中,您可以通过实例成员访问您的购物实例。因此,您可以使用购物模型临时存储计算总额,并使您的表单通信。在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
表单在self.forms:
如果不是form.is_valid():
返回#另外的错误存在,所以不要打扰
如果form.cleaned_data而不是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
表单在self.forms:
如果不是form.is_valid():
返回#另外的错误存在,所以不要打扰
如果form.cleaned_data和不是form.cleaned_data.get('DELETE'):
total + = form.cleaned_data ['cost']

#仅当Item内联格式也干净时才匹配
如果self.instance .__ total__不是None和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 *检查,因为表单也包含空的内联。
请让我知道这对你有用吗<​​/ p>

EDIT2:正确添加了要删除的表单的检查在评论中指出。这些表格不应该参与计算。


I am using Django 1.4 and I want to set validation rules that compare values of different inlines.

I have three simple classes

In 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)

In admin.py:

class ItemInline(admin.TabularInline):
    model = Item

class BuyerInline(admin.TabularInline):
    model = Buyer

class ShoppingAdmin(admin.ModelAdmin):
    inlines = (ItemInline, BuyerInline)

So for example it is possible to buy a bottle of rhum at 10$ and one of vodka at 8$. Mike pays 15$ and Tom pays 3$.

The goal is to prevent the user from saving an instance with sums that don't match: what has been paid must be the same as the sum of the item costs (ie 10+8 = 15+3).

I tried:

  • raising ValidationError in the Shopping.clean method. But the inlines aren't updated yet in clean so the sums are not correct
  • raising ValidationError in the ShoppingAdmin.save_related method. But raising ValidationError here gives a very user unfriendly error page instead of redirecting to the change page with a nice error message.

Is there any solution to this problem? Is client-side (javascript/ajax) validation the most simple?

解决方案

You could override your Inline formset to achieve what you want. In the clean method of the formset you have access to your Shopping instance through the 'instance' member. Therefore you could use the Shopping model to store the calculated total temporarily and make your formsets communicate. In 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

in 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

This is the only clean way you can do it (to the best of my knowledge) and everything is placed where it should be.

EDIT: Added the *if form.cleaned_data* check since forms contain empty inlines as well. Please let me know how this works for you!

EDIT2: Added the check for forms about to be deleted, as correctly pointed out in the comments. These forms should not participate in the calculations.

这篇关于在django管理员中验证依赖内联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆