55 votes

Validation de formulaire en ligne dans Django

Je voudrais rendre tout un formulaires en ligne dans un formulaire de changement d'administrateur obligatoire. Ainsi, dans mon scénario actuel, lorsque je clique sur Enregistrer dans un formulaire Facture (dans Admin), le formulaire de commande en ligne est vide. Je voudrais empêcher les gens de créer des factures sans commandes associées.

Quelqu'un sait un moyen facile de faire ça?

Une validation normale telle que ( required=True ) sur le champ du modèle ne semble pas fonctionner dans cette instance.

82voto

Daniel Roseman Points 199743

Pour ce faire, la meilleure solution consiste à définir un jeu de formulaires personnalisé, avec une méthode de nettoyage qui valide qu'au moins un ordre de facturation existe.

 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]
 

22voto

Dan Breen Points 4381

Daniel réponse est excellente et elle a fonctionné pour moi sur un projet, mais j'ai ensuite réalisé en raison de la façon Django formes de travail, si vous utilisez can_delete et cochez la case supprimer tout enregistrement, il est possible de valider w/o toutes les commandes (dans ce cas).

J'ai passé un certain temps à essayer de comprendre comment faire pour empêcher que cela se produise. La première situation est facile de ne pas inclure les formes qui vont être supprimés dans le comte. La deuxième situation est plus délicat...si tous les supprimer les cases sont cochées, puis clean n'a pas été appelé.

Le code n'est pas aussi simple que cela, malheureusement. L' clean méthode est appelée à partir d' full_clean qui est appelée lorsque l' error de la propriété est accessible. Cette propriété n'est pas accessible lorsqu'un sous-formulaire est supprimé, de sorte full_clean n'est jamais appelée. Je ne suis pas un Django d'experts, donc cela pourrait être une terrible façon de le faire, mais il semble fonctionner.

Voici la modification de la classe:

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')

4voto

Ahsan Points 3957

La solution de @Daniel Roseman est correcte mais j'ai quelques modifications à faire avec moins de code pour faire la même chose.

 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]
 

essayez ça marche aussi :)

4voto

Kurt Points 670
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  

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X