des vues basées sur des classes de django avec un model-form ou formset en ligne

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) 

puis traitez ceci via la vue standard.

Maintenant, je me demandais s’il y avait un moyen de parvenir à la même chose (c’est-à-dire: utiliser une ligne pour append / modifier des éléments appartenant à une facture) en utilisant des vues basées sur des classes (pas pour l’interface d’administration).

Les points clés sont:

  1. FormSet généré FormSet sein de forms.py utilisant inlineformset_factory :

     BookImageFormSet = inlineformset_factory(BookForm, BookImage, extra=2) BookPageFormSet = inlineformset_factory(BookForm, BookPage, extra=5) 
  2. a retourné le FormSet dans une classe views.py dans 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. form_valid utilisé 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)) 

Je viens d’append ma propre version après avoir vérifié certaines de ces CBV pré-faites. J’avais spécifiquement besoin de contrôler multiple formsets -> one parent dans une seule vue, chacun avec des fonctions de sauvegarde individuelles.

En gros, j’ai chargé la liaison de données FormSet dans une fonction get_named_formsets appelée par get_context_data et form_valid .

Là, je vérifie si tous les jeux de formulaires sont valides, et cherche également une méthode qui remplace une ancienne forme de formset.save() sur une base par formulaire pour une sauvegarde personnalisée.

Le modèle rend les formulaires 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() 

Vous devriez essayer django-extra-views . Recherchez CreateWithInlinesView et UpdateWithInlinesView .

Je redige 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 que cela peut être mis en œuvre rapidement.

Si vous regardez le module django.view.generic.edit (qui prend en charge la modification détaillée d’objects), utilisez le module django.view.generic.detail.

Je pense qu’un module django.view.generic.list_edit peut être implémenté à l’aide de django.view.generic.list et de django.view.generic.edit.

J’ai apporté quelques modifications à la solution originale pour laisser formset.is_valid () fonctionner:

  if self.request.POST: context['fs'] = MyInlineFS(self.request.POST, instance=self.object) else: context['fs'] = MyInlineFS(instance=self.object) 

Le code dans la réponse de Jordan n’a pas fonctionné pour moi. J’ai posté ma propre question à ce sujet, que je crois avoir résolue. Le premier argument de inlineformset_factory doit être Book, pas BookForm.

Je devais apporter une modification supplémentaire à la vue get_context_data() de Jordan et Speq pour que formset.non_form_errors existe dans le contexte du template.

 ... 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