2011-07-08 189 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