2011-07-08 186 views
2

更新:直接读取django源代码,我得到了一个无证失踪的部分来解决我的问题。感谢布兰登通过给我一件缺失的东西解决了一半的问题。看到我自己的答案,看看我的解决方案(我不想在这里混合的东西)。如何使用内联验证django-admin中两个模型的数据?

我有以下的(简化)型号:

Order(models.Model): 
    status = models.CharField(max_length=25, choices=STATUS_CHOICES, default='PENDING') 
    total = models.DecimalField(max_digits=22, decimal_places=2) 

    def clean(self): 
     if self.estatus == 'PAID' or self.estatus == 'SENT': 
      if len(self.payment.all()) > 0: 
       raise ValidationError("The status cannot be SENT or PAID if there is no payment for the order") 

Payment(models.Model): 
    amount = models.DecimalField(max_digits=22, decimal_places=2) 
    order = models.ForeignKey(Order, related_name="payment") 

    def clean(self): 
     if self.amount < self.order.total or self.amount <= 0: 
      ValidationError("The payment cannot be less than the order total") 

在我的admin.py我有:

class paymentInline(admin.StackedInline): 
    model = Payment 
    max_num = 1 

class OrderAdmin(admin.ModelAdmin): 
    model = Order 
    inlines = [ paymentInline, ] 

在订单的清洁方法验证不起作用,因为有验证发生时没有保存付款(显然它没有保存到数据库)。

付款内的验证工作正常(如果编辑或添加新的付款)。

我想验证订单是否有付款,如果状态是'PAID'或'SENT',但因为我不能这样做是在干净的方法。

我的问题是,如何访问用户在订单表单的内联(付款)中输入的'payment.amount'值,以完成我的验证? (在订单模式的清洁方法,考虑到IM)

回答

3

阅读Django的源代码后,我发现一个包含内嵌的父实例的BaseInlineFormSet的一个属性,在我的情况,订单情况正在编辑。

Brandon给了我另一件重要的事情,对BaseInlineFormSet的self.forms进行迭代,以获得每个实例(即使没有保存或未清理或空),在我的情况下,每个正在编辑的支付实例。

这些是检查状态为“PAID”或“SENT”的订单是否有付款所需的两条信息。遍历表单集的清单数据不会给出订单数据(即,当不更改订单,仅更改付款,或者不添加付款时 - 以及空付款 - 但更改订单),这是决定保存模型,如果订单状态不同于'PAID'或'SENT',则此方法在之前被丢弃。

的机型保持一致,我只修改了admin.py添加下一个:

class PaymentInlineFormset(forms.models.BaseInlineFormSet): 
    def clean(self): 
     order = None 
     payment = None 

     if any(self.errors): 
      return 

     # django/forms/models.py Line # 672,674 .. class BaseInlineFormSet(BaseModelFormSet) . Using Django 1.3 
     order = self.instance 

     #There should be only one form in the paymentInline (by design), so in the iteration below we end with a valid payment data. 
     if len(self.forms) > 1: 
      raise forms.ValidationError(u'Only one payment per order allowed.') 
     for f in self.forms: 
      payment = f.save(commit=False) 

     if payment.amount == None: 
      payment.amount = 0 

     if order != None: 
      if order.status in ['PAID', 'SENT'] and payment.amount <= 0: 
       raise forms.ValidationError(u'The order with %s status must have an associated payment.'%order.status) 

class pymentInline(admin.StackedInline): 
    model = Payment 
    max_num = 1 
    formset = PaymentInlineFormset 

class OrderAdmin(admin.ModelAdmin): 
    inlines = [ paymentInline, ] 

admin.site.register(Order, OrderAdmin) 
admin.site.register(Payment) 
0

这听起来像你只需要确认有在行内至少有一个有效的formset ......你可以给这个代码试试:http://wadofstuff.blogspot.com/2009/08/requiring-at-least-one-inline-formset.html

希望能让你走。

[编辑]

我看了看一些代码,我已经为其他应用程序基于相关模型的属性是对的内嵌自定义验证写的。当然,您可能需要进行一些调整,但请试一试。您还需要指定一个内联以在您的管理模型中使用。

#I would put this in your app's admin.py 
class PaymentInline(admin.TabularInline): 
    model = Payment 
    formset = PaymentInlineFormset 

#I would put this in the app's forms.py 
class PaymentInlineFormset(forms.models.BaseInlineFormSet): 
     def clean(self): 
      order = None 
      valid_forms = 0 

      for error in self.errors: 
       if error: 
        return 

      for cleaned_data in self.cleaned_data: 
       amount = cleaned_data.get('amount', 0) 
       if order == None: 
        order = cleaned_data.get('order') 
       if amount > 0: 
        valid_forms += 1 

      if order.status in ['PAID', 'SENT'] and len(valid_forms) > 0: 
       raise forms.ValidationError(u'Your error message') 
+0

我只需要requiere的内联有效的formset当订单的状态是“付费”或'SENT'。其他任何状态都可以让用户保存没有内嵌数据的模型(空的数据)。问题是如何从每个模型表单(正在编辑的主模型和内联表单)访问这些字段(数据)以进行比较。 – automatito

+0

应该可以获取对内联关联的父对象的引用。我明天会看看我能否为你准备好样品。 – Brandon

+0

我添加了一个显示可能适合您的自定义内联窗体集的编辑。 – Brandon