Как проверить данные двух моделей в django-admin при использовании inlines? - PullRequest
2 голосов
/ 08 июля 2011

Обновление : читая непосредственно исходный код 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, введенному пользователем во встроенную (платежную) форму заказа, чтобы выполнить мою проверку? (учитывая im в чистом методе модели Order)

Ответы [ 2 ]

3 голосов
/ 11 июля 2011

После прочтения исходного кода django я нашел одно свойство BaseInlineFormSet, которое содержит родительский экземпляр Inline, в моем случае редактируемый экземпляр Order.

Брэндон дал мне еще одну важную деталь, перебирая self.forms объекта BaseInlineFormSet, чтобы получить каждый из экземпляров (даже не сохраненных, не очищенных или пустых), в моем случае - каждый редактируемый экземпляр платежа.

Это две части информации, необходимые для проверки того, имеет ли Заказ со статусом «ОПЛАТЕНО» или «ОТПРАВЛЕНО» платеж или нет. Итерация по cleaned_data набора форм не выдала бы данные Заказа (т. Е. Когда не изменили Заказ, просто изменили Платеж или не добавили Платеж - и пустой Платеж - но изменили Заказ), который необходим для принятия решения о сохранении модель, если статус заказа отличается от «Оплачено» или «Отправлено», поэтому этот метод был отменен ранее.

Модели остались прежними, я только изменил 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 голосов
/ 08 июля 2011

Похоже, вам просто нужно проверить, что в строках есть хотя бы один действительный набор форм ... вы можете попробовать этот код: 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')
...