web-dev-qa-db-fra.com

Django: plusieurs modèles dans un modèle à l'aide de formulaires

Je construis une application de suivi des tickets de support et j'ai quelques modèles que je voudrais créer à partir d'une page. Les billets appartiennent à un client via une clé étrangère. Les notes appartiennent également aux Billets via une ForeignKey. Je voudrais avoir la possibilité de sélectionner un client (c'est un projet entièrement séparé) OR créer un nouveau client, puis créer un ticket et enfin créer une note affectée au nouveau ticket.

Django étant relativement nouveau, j’ai tendance à travailler de manière itérative, à essayer de nouvelles fonctionnalités à chaque fois. J'ai joué avec ModelForms mais je souhaite masquer certains champs et effectuer des validations complexes. Il semble que le niveau de contrôle que je recherche requiert des jeux de formulaires ou tout à la main, complété par une page de modèle fastidieuse et codée à la main, ce que j'essaie d'éviter.

Y a-t-il une belle fonctionnalité qui me manque? Est-ce que quelqu'un a une bonne référence ou un bon exemple pour utiliser des formsets? J'ai passé tout un week-end sur les documents API pour eux et je ne suis toujours pas au courant. Est-ce un problème de conception si je décompose et code à la main tout?

106
neoice

Ce n'est vraiment pas trop difficile à implémenter avec ModelForms . Supposons donc que vous avez les formulaires A, B et C. Vous imprimez chacun des formulaires et la page et vous devez maintenant gérer le POST.

if request.POST():
    a_valid = formA.is_valid()
    b_valid = formB.is_valid()
    c_valid = formC.is_valid()
    # we do this since 'and' short circuits and we want to check to whole page for form errors
    if a_valid and b_valid and c_valid:
        a = formA.save()
        b = formB.save(commit=False)
        c = formC.save(commit=False)
        b.foreignkeytoA = a
        b.save()
        c.foreignkeytoB = b
        c.save()

Ici sont les documents pour la validation personnalisée.

78
Jason Christa

Je me trouvais dans la même situation il y a un jour et voici mes 2 centimes:

1) J'ai trouvé sans doute la démonstration la plus courte et la plus concise de la saisie de modèles multiples sous une forme unique ici: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-Django- avec-newforms / .

En un mot: Créez un formulaire pour chaque modèle, soumettez-les tous les deux au modèle en un seul <form>, en utilisant prefix keyarg et demandez à la vue de gérer la validation. S'il y a dépendance, assurez-vous simplement de sauvegarder le modèle "parent" avant dépendant, et utilisez l'ID du parent pour la clé étrangère avant de valider la sauvegarde du modèle "enfant". Le lien a la démo.

2) Peut-être que des formulaires peuvent être utilisés pour le faire, mais dans la mesure où j'ai approfondi, les jeux de formulaires servent principalement à entrer des multiples du même modèle, ce qui peut être éventuellement lié à un autre modèle/modèles par des clés étrangères. Cependant, il semble n'y avoir aucune option par défaut pour saisir plus de données d'un modèle, et ce n'est pas ce à quoi le formset semble destiné.

63
Gnudiff

J'ai eu récemment un problème et je viens de comprendre comment faire cela. En supposant que vous avez trois classes, primaire, B, C et que B, C ont une clé étrangère à primaire

    class PrimaryForm(ModelForm):
        class Meta:
            model = Primary

    class BForm(ModelForm):
        class Meta:
            model = B
            exclude = ('primary',)

    class CForm(ModelForm):
         class Meta:
            model = C
            exclude = ('primary',)

    def generateView(request):
        if request.method == 'POST': # If the form has been submitted...
            primary_form = PrimaryForm(request.POST, prefix = "primary")
            b_form = BForm(request.POST, prefix = "b")
            c_form = CForm(request.POST, prefix = "c")
            if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
                    print "all validation passed"
                    primary = primary_form.save()
                    b_form.cleaned_data["primary"] = primary
                    b = b_form.save()
                    c_form.cleaned_data["primary"] = primary
                    c = c_form.save()
                    return HttpResponseRedirect("/viewer/%s/" % (primary.name))
            else:
                    print "failed"

        else:
            primary_form = PrimaryForm(prefix = "primary")
            b_form = BForm(prefix = "b")
            c_form = Form(prefix = "c")
     return render_to_response('multi_model.html', {
     'primary_form': primary_form,
     'b_form': b_form,
     'c_form': c_form,
      })

Cette méthode devrait vous permettre de faire la validation requise et de générer les trois objets sur la même page. J'ai également utilisé du javascript et des champs cachés pour permettre la génération de plusieurs objets B, C sur la même page.

25
ErgoSum

Le MultiModelForm de Django-betterforms est un emballage pratique pour faire ce qui est décrit dans réponse de Gnudiff . Il encapsule les ModelForms ordinaires dans une classe unique qui est utilisée de manière transparente (au moins pour l’utilisation de base) sous forme de formulaire unique. J'ai copié un exemple de leur document ci-dessous.

# forms.py
from Django import forms
from Django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile

User = get_user_model()

class UserEditForm(forms.ModelForm):
    class Meta:
        fields = ('email',)

class UserProfileForm(forms.ModelForm):
    class Meta:
        fields = ('favorite_color',)

class UserEditMultiForm(MultiModelForm):
    form_classes = {
        'user': UserEditForm,
        'profile': UserProfileForm,
    }

# views.py
from Django.views.generic import UpdateView
from Django.core.urlresolvers import reverse_lazy
from Django.shortcuts import redirect
from Django.contrib.auth import get_user_model
from .forms import UserEditMultiForm

User = get_user_model()

class UserSignupView(UpdateView):
    model = User
    form_class = UserEditMultiForm
    success_url = reverse_lazy('home')

    def get_form_kwargs(self):
        kwargs = super(UserSignupView, self).get_form_kwargs()
        kwargs.update(instance={
            'user': self.object,
            'profile': self.object.profile,
        })
        return kwargs
9
jozxyqk

J'ai actuellement une solution de contournement fonctionnelle (il passe mes tests unitaires). C'est une bonne solution à mon avis lorsque vous souhaitez uniquement ajouter un nombre limité de champs d'autres modèles.

Est-ce que j'ai râté quelque chose ?

class UserProfileForm(ModelForm):
    def __init__(self, instance=None, *args, **kwargs):
        # Add these fields from the user object
        _fields = ('first_name', 'last_name', 'email',)
        # Retrieve initial (current) data from the user object
        _initial = model_to_dict(instance.user, _fields) if instance is not None else {}
        # Pass the initial data to the base
        super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
        # Retrieve the fields from the user model and update the fields with it
        self.fields.update(fields_for_model(User, _fields))

    class Meta:
        model = UserProfile
        exclude = ('user',)

    def save(self, *args, **kwargs):
        u = self.instance.user
        u.first_name = self.cleaned_data['first_name']
        u.last_name = self.cleaned_data['last_name']
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(UserProfileForm, self).save(*args,**kwargs)
        return profile
5
Paul Bormans

"Je souhaite masquer certains champs et effectuer des validations complexes."

Je commence par l'interface d'administration intégrée.

  1. Construisez le ModelForm pour afficher les champs souhaités.

  2. Étendez le formulaire avec les règles de validation dans le formulaire. Il s’agit généralement d’une méthode clean.

    Assurez-vous que cette partie fonctionne assez bien.

Une fois que cela est fait, vous pouvez vous éloigner de l'interface d'administration intégrée.

Ensuite, vous pouvez vous amuser avec plusieurs formulaires partiellement liés sur une même page Web. Ceci est un tas de modèles pour présenter tous les formulaires sur une seule page.

Ensuite, vous devez écrire la fonction de vue pour lire et valider les différentes choses de la forme et faire les différentes sauvegardes d'objet ().

"Est-ce un problème de conception si je décompose et code à la main tout?" Non, c'est juste beaucoup de temps pour peu d'avantages.

3
S.Lott

Selon la documentation de Django, les jeux de formulaires en ligne sont utilisés à cette fin: "Les jeux de formulaires en ligne sont une petite couche d’abstraction au-dessus des jeux de modèles. Elles simplifient le cas de travail avec des objets liés via une clé étrangère".

Voir https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets

0
user1376892