-
-
Save badri/4a1be2423ce9353373e1b3f2cc67b80b to your computer and use it in GitHub Desktop.
| {% extends "base.html" %} | |
| {% block content %} | |
| <form method="post">{% csrf_token %} | |
| {{ forms.subscription }} | |
| <input type="submit" value="Subscribe"> | |
| </form> | |
| <form method="post">{% csrf_token %} | |
| {{ forms.contact }} | |
| <input type="submit" value="Send"> | |
| </form> | |
| {% endblock content %} |
| from django import forms | |
| class MultipleForm(forms.Form): | |
| action = forms.CharField(max_length=60, widget=forms.HiddenInput()) | |
| class ContactForm(MultipleForm): | |
| title = forms.CharField(max_length=150) | |
| message = forms.CharField(max_length=200, widget=forms.TextInput) | |
| class SubscriptionForm(MultipleForm): | |
| email = forms.EmailField() |
| from django.views.generic.base import ContextMixin, TemplateResponseMixin | |
| from django.views.generic.edit import ProcessFormView | |
| class MultiFormMixin(ContextMixin): | |
| form_classes = {} | |
| prefixes = {} | |
| success_urls = {} | |
| initial = {} | |
| prefix = None | |
| success_url = None | |
| def get_form_classes(self): | |
| return self.form_classes | |
| def get_forms(self, form_classes): | |
| return dict([(key, self._create_form(key, class_name)) \ | |
| for key, class_name in form_classes.items()]) | |
| def get_form_kwargs(self, form_name): | |
| kwargs = {} | |
| kwargs.update({'initial':self.get_initial(form_name)}) | |
| kwargs.update({'prefix':self.get_prefix(form_name)}) | |
| if self.request.method in ('POST', 'PUT'): | |
| kwargs.update({ | |
| 'data': self.request.POST, | |
| 'files': self.request.FILES, | |
| }) | |
| return kwargs | |
| def forms_valid(self, forms, form_name): | |
| form_valid_method = '%s_form_valid' % form_name | |
| if hasattr(self, form_valid_method): | |
| return getattr(self, form_valid_method)(forms[form_name]) | |
| else: | |
| return HttpResponseRedirect(self.get_success_url(form_name)) | |
| def forms_invalid(self, forms): | |
| return self.render_to_response(self.get_context_data(forms=forms)) | |
| def get_initial(self, form_name): | |
| initial_method = 'get_%s_initial' % form_name | |
| if hasattr(self, initial_method): | |
| return getattr(self, initial_method)() | |
| else: | |
| return {'action': form_name} | |
| def get_prefix(self, form_name): | |
| return self.prefixes.get(form_name, self.prefix) | |
| def get_success_url(self, form_name=None): | |
| return self.success_urls.get(form_name, self.success_url) | |
| def _create_form(self, form_name, form_class): | |
| form_kwargs = self.get_form_kwargs(form_name) | |
| form = form_class(**form_kwargs) | |
| return form | |
| class ProcessMultipleFormsView(ProcessFormView): | |
| def get(self, request, *args, **kwargs): | |
| form_classes = self.get_form_classes() | |
| forms = self.get_forms(form_classes) | |
| return self.render_to_response(self.get_context_data(forms=forms)) | |
| def post(self, request, *args, **kwargs): | |
| form_classes = self.get_form_classes() | |
| form_name = request.POST.get('action') | |
| return self._process_individual_form(form_name, form_classes) | |
| def _process_individual_form(self, form_name, form_classes): | |
| forms = self.get_forms(form_classes) | |
| form = forms.get(form_name) | |
| if not form: | |
| return HttpResponseForbidden() | |
| elif form.is_valid(): | |
| return self.forms_valid(forms, form_name) | |
| else: | |
| return self.forms_invalid(forms) | |
| class BaseMultipleFormsView(MultiFormMixin, ProcessMultipleFormsView): | |
| """ | |
| A base view for displaying several forms. | |
| """ | |
| class MultiFormsView(TemplateResponseMixin, BaseMultipleFormsView): | |
| """ | |
| A view for displaying several forms, and rendering a template response. | |
| """ |
| from django.http import HttpResponse, HttpResponseRedirect | |
| from django.shortcuts import render | |
| from django.urls import reverse, reverse_lazy | |
| from .forms import ContactForm, SubscriptionForm | |
| from .multiforms import MultiFormsView | |
| def form_redir(request): | |
| return render(request, 'pages/form_redirect.html') | |
| def multiple_forms(request): | |
| if request.method == 'POST': | |
| contact_form = ContactForm(request.POST) | |
| subscription_form = SubscriptionForm(request.POST) | |
| if contact_form.is_valid() or subscription_form.is_valid(): | |
| # Do the needful | |
| return HttpResponseRedirect(reverse('form-redirect') ) | |
| else: | |
| contact_form = ContactForm() | |
| subscription_form = SubscriptionForm() | |
| return render(request, 'pages/multiple_forms.html', { | |
| 'contact_form': contact_form, | |
| 'subscription_form': subscription_form, | |
| }) | |
| class MultipleFormsDemoView(MultiFormsView): | |
| template_name = "pages/cbv_multiple_forms.html" | |
| form_classes = {'contact': ContactForm, | |
| 'subscription': SubscriptionForm, | |
| } | |
| success_urls = { | |
| 'contact': reverse_lazy('form-redirect'), | |
| 'subscription': reverse_lazy('form-redirect'), | |
| } | |
| def contact_form_valid(self, form): | |
| title = form.cleaned_data.get('title') | |
| form_name = form.cleaned_data.get('action') | |
| print(title) | |
| return HttpResponseRedirect(self.get_success_url(form_name)) | |
| def subscription_form_valid(self, form): | |
| email = form.cleaned_data.get('email') | |
| form_name = form.cleaned_data.get('action') | |
| print(email) | |
| return HttpResponseRedirect(self.get_success_url(form_name)) |
Thank you. Very useful.
Two small additions. First the instance feature...:def get_form_kwargs(self, form_name): kwargs = {} kwargs.update({'instance': self.get_instance(form_name)}) # helps when updating records kwargs.update({'initial': self.get_initial(form_name)}) kwargs.update({'prefix': self.get_prefix(form_name)})and the associated method
def get_instance(self, form_name): instance_method = 'get_%s_instance' % form_name if hasattr(self, instance_method): return getattr(self, instance_method)() else: return NoneThen fix an issue with initial values not containing an action, when the get_initial was called, and causing 403 from _process_individual_form. The solution is to add action in the get_initial() on MultiFormMixin.
def get_initial(self, form_name): initial_method = 'get_%s_initial' % form_name if hasattr(self, initial_method): attrs = getattr(self, initial_method)() attrs['action'] = form_name return attrs else: return {'action': form_name}Thanks again.
Hi,
I had the same issue that you mentioned in the latter part, that form_classes was being returned as an empty list. I replaced the get_initial function as you suggested, but it's still giving the same issue.
Did you do anything else to fix the problem?
Thank you this has been very insightful 😃
One little Fix though to the form_is_valid
Change forms_valid method to
def forms_valid(self, forms, form_name):
form_valid_method = '%s_valid' % form_name
if hasattr(self, form_valid_method):
return getattr(self, form_valid_method)(forms[form_name])
else:
form = forms.get(form_name)
form.save()
return HttpResponseRedirect(self.get_success_url(form_name))
Explanation
What has changed is form_valid_method = '%s_form_valid' % form_name to form_valid_method = '%s_valid' % form_name.
Assuming you have a form called user_form the former case form_valid_method would be form_valid_method = 'user_form_form_valid'
with that you would have ended in the else block.
Thank you. Very useful. Two small additions. First the instance feature...:
def get_form_kwargs(self, form_name): kwargs = {} kwargs.update({'instance': self.get_instance(form_name)}) # helps when updating records kwargs.update({'initial': self.get_initial(form_name)}) kwargs.update({'prefix': self.get_prefix(form_name)})and the associated method
def get_instance(self, form_name): instance_method = 'get_%s_instance' % form_name if hasattr(self, instance_method): return getattr(self, instance_method)() else: return NoneThen fix an issue with initial values not containing an action, when the get_initial was called, and causing 403 from _process_individual_form. The solution is to add action in the get_initial() on MultiFormMixin.
def get_initial(self, form_name): initial_method = 'get_%s_initial' % form_name if hasattr(self, initial_method): attrs = getattr(self, initial_method)() attrs['action'] = form_name return attrs else: return {'action': form_name}Thanks again.
Hi!
I had the same issue with 403. Tried to use your updates, but getting an error:
TypeError: init() got an unexpected keyword argument 'instance'
What could be the problem?
@JulieGoldberg Hi, can you help me with a question?
i'm trying to save a form called vendor_form, but he also uses fields from user_form, so my valid method is "if user_form.is_valid() and vendor_form.is_valid()'
since i depend the user to create the vendor user, i'm getting null value collumn error because for some reason the user_form is not getting saved cause the action is 'vendor: vendor_form'
Thans for cool job, Lakshmi!
I mixed your idea up with generic.UpdateView.
also we need to add:
then, now we can code like: