Проверка валидной формы в Джанго - PullRequest
48 голосов
/ 18 мая 2009

Я бы хотел сделать весь встроенный набор форм в форме изменения администратора обязательным. Таким образом, в моем текущем сценарии, когда я нажимаю «Сохранить» в форме «Счет-фактура» (в Admin), встроенная форма «Заказ» пуста. Я бы хотел, чтобы люди не создавали счета-фактуры без связанных с ними заказов.

Кто-нибудь знает простой способ сделать это?

Обычная проверка, например (required=True) для поля модели, в этом случае не работает.

Ответы [ 5 ]

69 голосов
/ 18 мая 2009

Лучший способ сделать это - определить пользовательский набор форм с чистым методом, который проверяет, существует ли хотя бы один порядок выставления счетов.

class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
    def clean(self):
        # get forms that actually have valid data
        count = 0
        for form in self.forms:
            try:
                if form.cleaned_data:
                    count += 1
            except AttributeError:
                # annoyingly, if a subform is invalid Django explicity raises
                # an AttributeError for cleaned_data
                pass
        if count < 1:
            raise forms.ValidationError('You must have at least one order')

class InvoiceOrderInline(admin.StackedInline):
    formset = InvoiceOrderInlineFormset


class InvoiceAdmin(admin.ModelAdmin):
    inlines = [InvoiceOrderInline]
20 голосов
/ 11 декабря 2009

Ответ Даниэля превосходен, и он работал для меня в одном проекте, но потом я понял, что из-за того, как работают формы Django, если вы используете can_delete и устанавливаете флажок удаления при сохранении, можно проверить без каких-либо заказов (в данном случае).

Я потратил некоторое время, пытаясь понять, как этого избежать. Первая ситуация была простой - не включайте в счет формы, которые будут удалены. Вторая ситуация была хитрее ... если all флажки удаления установлены, то clean не вызывается.

Код, к сожалению, не совсем простой. Метод clean вызывается из full_clean, который вызывается при обращении к свойству error. Это свойство недоступно при удалении подчиненной формы, поэтому full_clean никогда не вызывается. Я не эксперт по Django, так что это может быть ужасный способ сделать это, но это похоже на работу.

Вот модифицированный класс:

class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
    def is_valid(self):
        return super(InvoiceOrderInlineFormset, self).is_valid() and \
                    not any([bool(e) for e in self.errors])

    def clean(self):
        # get forms that actually have valid data
        count = 0
        for form in self.forms:
            try:
                if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
                    count += 1
            except AttributeError:
                # annoyingly, if a subform is invalid Django explicity raises
                # an AttributeError for cleaned_data
                pass
        if count < 1:
            raise forms.ValidationError('You must have at least one order')
4 голосов
/ 20 июля 2011

@ Даниэль Роузман в порядке, но у меня есть некоторые модификации с меньшим количеством кода, чтобы сделать то же самое.

class RequiredFormSet(forms.models.BaseInlineFormSet):
      def __init__(self, *args, **kwargs):
          super(RequiredFormSet, self).__init__(*args, **kwargs)
          self.forms[0].empty_permitted = False

class InvoiceOrderInline(admin.StackedInline):
      model = InvoiceOrder
      formset = RequiredFormSet


class InvoiceAdmin(admin.ModelAdmin):
     inlines = [InvoiceOrderInline]

попробуйте это тоже работает:)

3 голосов
/ 03 июня 2010
class MandatoryInlineFormSet(BaseInlineFormSet):  

    def is_valid(self):
        return super(MandatoryInlineFormSet, self).is_valid() and \
                    not any([bool(e) for e in self.errors])  
    def clean(self):          
        # get forms that actually have valid data
        count = 0
        for form in self.forms:
            try:
                if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
                    count += 1
            except AttributeError:
                # annoyingly, if a subform is invalid Django explicity raises
                # an AttributeError for cleaned_data
                pass
        if count < 1:
            raise forms.ValidationError('You must have at least one of these.')  

class MandatoryTabularInline(admin.TabularInline):  
    formset = MandatoryInlineFormSet

class MandatoryStackedInline(admin.StackedInline):  
    formset = MandatoryInlineFormSet

class CommentInlineFormSet( MandatoryInlineFormSet ):

    def clean_rating(self,form):
        """
        rating must be 0..5 by .5 increments
        """
        rating = float( form.cleaned_data['rating'] )
        if rating < 0 or rating > 5:
            raise ValidationError("rating must be between 0-5")

        if ( rating / 0.5 ) != int( rating / 0.5 ):
            raise ValidationError("rating must have .0 or .5 decimal")

    def clean( self ):

        super(CommentInlineFormSet, self).clean()

        for form in self.forms:
            self.clean_rating(form)


class CommentInline( MandatoryTabularInline ):  
    formset = CommentInlineFormSet  
    model = Comment  
    extra = 1  
2 голосов
/ 06 апреля 2018

Ситуация стала немного лучше, но все еще нужно немного обойти. Django предоставляет validate_min и min_num attrs в настоящее время, и если min_num взято из Inline во время мгновенного набора форм, validate_min может быть передано только как аргумент init formset. Итак, решение выглядит примерно так:

class MinValidatedInlineMixIn:
    validate_min = True
    def get_formset(self, *args, **kwargs):
        return super().get_formset(validate_min=self.validate_min, *args, **kwargs)

class InvoiceOrderInline(MinValidatedInlineMixIn, admin.StackedInline):
    model = InvoiceOrder
    min_num = 1
    validate_min = True

class InvoiceAdmin(admin.ModelAdmin):
    inlines = [InvoiceOrderInline]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...