Подтвердить что ты не робот

Проверка встроенной формы в Django

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

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

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

4b9b3361

Ответ 1

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

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]

Ответ 2

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

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

Ответ 3

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

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]

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

Ответ 4

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  

Ответ 5

Ситуация стала немного лучше, но все еще нужно немного поработать. Django предоставляет min_num validate_min и min_num и если 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]