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).
7 réponses
points Clés est la suivante:
-
généré
FormSet
à l'intérieurforms.py
à l'aideinlineformset_factory
:BookImageFormSet = inlineformset_factory(BookForm, BookImage, extra=2) BookPageFormSet = inlineformset_factory(BookForm, BookPage, extra=5)
-
retourné
FormSet
s au sein d'unCreateView
classeviews.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
-
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))
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()
Vous devriez essayer django-extra-vues . Cherchez CreateWithInlinesView
et UpdateWithInlinesView
.
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.
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)
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.
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