-
-
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 None
Then 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'
Thank you this has been very insightful 😃
One little Fix though to the
form_is_valid
Change
forms_valid
method toExplanation
What has changed is
form_valid_method = '%s_form_valid' % form_name
toform_valid_method = '%s_valid' % form_name
.Assuming you have a form called
user_form
the former caseform_valid_method
would beform_valid_method = 'user_form_form_valid'
with that you would have ended in the else block.