更新:直接读取django源代码我得到了一个未记录的缺失部分来解决我的问题。感谢布兰登通过给我一个缺失的部分解决了一半的问题。看看我自己的答案,看看我的解决方案(我不想在这里混合一些东西)。
我有以下(简化)模型:
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, ]
订单的清洁方法中的验证不起作用,因为验证发生时没有保存付款(显然它尚未保存到数据库中)。
付款内的验证工作正常(如果编辑或添加新付款)。
我想验证订单是否有付款,如果状态是'付款'或'发送',但因为我不能这样做的方式是干净的方法。
我的问题是,如何在订单表格的内联(付款)中访问用户输入的'payment.amount'值,以完成我的验证? (考虑我在订单模型的清洁方法中)
答案 0 :(得分:3)
在阅读django源代码后,我发现BaseInlineFormSet的一个属性包含Inline的Parent实例,在我的例子中,正在编辑的Order实例。
布兰登给了我另一个重要的部分,迭代了BaseInlineFormSet的self.forms以获取每个实例(甚至没有保存或未清除或清空),在我的情况下,每个付款实例都被编辑。
这些是检查状态为“付款”或“发送”的订单是否有付款所需的两条信息。迭代formset的cleaning_data不会给订单数据(即不更改订单,只更改付款,或者不添加付款 - 和空付款 - 但更改订单),这是决定保存如果订单状态不同于'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)
答案 1 :(得分: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')