django la classe de base des points de vue avec inline-modèle de formulaire ou un formset

j'ai les modèles suivants:

class Bill(models.Model):
    date = models.DateTimeField(_("Date of bill"),null=True,blank=True)

class Item(models.Model):
    name = models.CharField(_("Name"),max_length=100)
    price = models.FloatField(_("Price"))
    quantity = models.IntegerField(_("Quantity"))
    bill = models.ForeignKey("Bill",verbose_name=_("Bill"),
                             related_name="billitem")

je sais que c'est possible:

from django.forms.models import inlineformset_factory
inlineformset_factory(Bill, Item)

et puis traiter cela via la vue standard.

maintenant je me demandais s'il y avait un moyen d'obtenir la même chose (c'est-à-dire: utiliser un inline pour ajouter/éditer des éléments appartenant à une facture) en utilisant class based views (pas pour l'interface d'administration).

51
demandé sur Hixi 2010-12-21 12:07:54

7 réponses

points Clés est la suivante:

  1. généré FormSet à l'intérieur forms.py à l'aide inlineformset_factory :

    BookImageFormSet = inlineformset_factory(BookForm, BookImage, extra=2)
    BookPageFormSet = inlineformset_factory(BookForm, BookPage, extra=5)
    
  2. retourné FormSet s au sein d'un CreateView classe views.py :

    def get_context_data(self, **kwargs):
        context = super(BookCreateView, self).get_context_data(**kwargs)
        if self.request.POST:
            context['bookimage_form'] = BookImageFormSet(self.request.POST)
            context['bookpage_form'] = BookPageFormSet(self.request.POST)
        else:
            context['bookimage_form'] = BookImageFormSet()
            context['bookpage_form'] = BookPageFormSet()
        return context
    
  3. Utilisé form_valid pour enregistrer le formulaire et formset:

     def form_valid(self, form):
         context = self.get_context_data()
         bookimage_form = context['bookimage_formset']
         bookpage_form = context['bookpage_formset']
         if bookimage_form.is_valid() and bookpage_form.is_valid():
             self.object = form.save()
             bookimage_form.instance = self.object
             bookimage_form.save()
             bookpage_form.instance = self.object
             bookpage_form.save()
             return HttpResponseRedirect('thanks/')
         else:
             return self.render_to_response(self.get_context_data(form=form))
    
56
répondu Jordan Reiter 2015-04-09 10:36:58

je viens d'ajouter ma propre version après avoir vérifié quelques-unes de ces CBV pré-faites. J'ai spécifiquement eu besoin de contrôle sur multiple formsets -> one parent dans une vue unique chacun avec des fonctions de sauvegarde individuelles.

j'ai essentiellement rempli les données FormSet liant dans une fonction get_named_formsets qui est appelé par get_context_data et form_valid .

là, je vérifie si tous les Forms sont valides, et aussi chercher une méthode qui remplace un vieux formset.save() sur un per formset base for custom saving.

le modèle rend formsets via

{% with named_formsets.my_specific_formset as formset %}
 {{ formset }}
 {{ formset.management_form }}
{% endwith %}

je pense que je vais utiliser ce système régulièrement.

class MyView(UpdateView): # FormView, CreateView, etc
  def get_context_data(self, **kwargs):
        ctx = super(MyView, self).get_context_data(**kwargs)
        ctx['named_formsets'] = self.get_named_formsets()
        return ctx

    def get_named_formsets(self):
        return {
            'followup': FollowUpFormSet(self.request.POST or None, prefix='followup'),
            'action': ActionFormSet(self.request.POST or None, prefix='action'),
        }

    def form_valid(self, form):
        named_formsets = self.get_named_formsets()
        if not all((x.is_valid() for x in named_formsets.values())):
            return self.render_to_response(self.get_context_data(form=form))

        self.object = form.save()

        # for every formset, attempt to find a specific formset save function
        # otherwise, just save.
        for name, formset in named_formsets.items():
            formset_save_func = getattr(self, 'formset_{0}_valid'.format(name), None)
            if formset_save_func is not None:
                formset_save_func(formset)
            else:
                formset.save()
        return http.HttpResponseRedirect('')

    def formset_followup_valid(self, formset):
        """
        Hook for custom formset saving.. useful if you have multiple formsets
        """
        followups = formset.save(commit=False) # self.save_formset(formset, contact)
        for followup in followups:
            followup.who = self.request.user
            followup.contact = self.object
            followup.save()
14
répondu Yuji 'Tomita' Tomita 2012-08-10 23:24:42

Vous devriez essayer django-extra-vues . Cherchez CreateWithInlinesView et UpdateWithInlinesView .

7
répondu Udi 2013-11-03 19:38:35

indique le code source générique du 1.3-beta-1:

le code n'est absolument pas prêt pour l'édition de liste ou il y a de la magie noire ici. Mais je pense qu'il peut être mis en œuvre rapidement.

Si vous regardez le django.vue.générique.edit (qui supporte l'édition détaillée d'objet) module comment utiliser le django.vue.générique.détail du module.

je pense que c'est un django.vue.générique.le module list_edit peut être implémenté en utilisant django.vue.générique.liste et une partie de django.vue.générique.modifier.

1
répondu VGE 2011-01-28 10:04:42

j'ai fait quelques modifications à la solution originale pour laisser formset.is_valid () to work:

    if self.request.POST:
        context['fs'] = MyInlineFS(self.request.POST, instance=self.object)
    else:
        context['fs'] = MyInlineFS(instance=self.object)
1
répondu Speq 2011-10-22 07:37:56

le code dans la réponse de Jordan ne marchait pas pour moi. J'ai posté ma propre question à ce sujet, que je crois avoir maintenant résolu. Le premier argument pour inlineformset_factory devrait être Book, et non BookForm.

1
répondu knite 2017-05-23 11:46:37

j'ai dû apporter une autre modification au get_context_data() de Jordan et Speq pour que formset.non_form_errors existe dans le contexte du modèle.

...
if self.request.POST:
    context['fs'] = MyInlineFS(self.request.POST, instance=self.object)
    context['fs'].full_clean()  # <-- new
else:
    context['fs'] = MyInlineFS(instance=self.object)
return context
1
répondu pztrick 2013-01-30 16:10:25