Skip to content

Instantly share code, notes, and snippets.

@jamesbrobb
Forked from michelts/gist:1029336
Last active July 4, 2023 18:53
Show Gist options
  • Save jamesbrobb/748c47f46b9bd224b07f to your computer and use it in GitHub Desktop.
Save jamesbrobb/748c47f46b9bd224b07f to your computer and use it in GitHub Desktop.
django multiform mixin and view that allows the submission of a) All forms b) Grouped forms c) An individual form
class MultiFormMixin(ContextMixin):
form_classes = {}
prefixes = {}
success_urls = {}
grouped_forms = {}
initial = {}
prefix = None
success_url = None
def get_form_classes(self):
return self.form_classes
def get_forms(self, form_classes, form_names=None, bind_all=False):
return dict([(key, self._create_form(key, klass, (form_names and key in form_names) or bind_all)) \
for key, klass in form_classes.items()])
def get_form_kwargs(self, form_name, bind_form=False):
kwargs = {}
kwargs.update({'initial':self.get_initial(form_name)})
kwargs.update({'prefix':self.get_prefix(form_name)})
if bind_form:
kwargs.update(self._bind_form_data())
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 self.initial.copy()
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, klass, bind_form):
form_kwargs = self.get_form_kwargs(form_name, bind_form)
form_create_method = 'create_%s_form' % form_name
if hasattr(self, form_create_method):
form = getattr(self, form_create_method)(**form_kwargs)
else:
form = klass(**form_kwargs)
return form
def _bind_form_data(self):
if self.request.method in ('POST', 'PUT'):
return{'data': self.request.POST,
'files': self.request.FILES,}
return {}
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')
if self._individual_exists(form_name):
return self._process_individual_form(form_name, form_classes)
elif self._group_exists(form_name):
return self._process_grouped_forms(form_name, form_classes)
else:
return self._process_all_forms(form_classes)
def _individual_exists(self, form_name):
return form_name in self.form_classes
def _group_exists(self, group_name):
return group_name in self.grouped_forms
def _process_individual_form(self, form_name, form_classes):
forms = self.get_forms(form_classes, (form_name,))
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)
def _process_grouped_forms(self, group_name, form_classes):
form_names = self.grouped_forms[group_name]
forms = self.get_forms(form_classes, form_names)
if all([forms.get(form_name).is_valid() for form_name in form_names.values()]):
return self.forms_valid(forms)
else:
return self.forms_invalid(forms)
def _process_all_forms(self, form_classes):
forms = self.get_forms(form_classes, None, True)
if all([form.is_valid() for form in forms.values()]):
return self.forms_valid(forms)
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.
"""
@AustinNotAustin
Copy link

@PBRWJELF

Any such luck?

@leogenius360
Copy link

leogenius360 commented Aug 7, 2022

Please @jamesbrobb could you show to override the save() method and an example for the post method?
How to override the post() method in the view.py to actually save an object to the database

I tried this but it's not working

class OrderFormView(LoginRequiredMixin, SuccessMessageMixin, MultiFormsView):
template_name = '_order.html'
form_classes = {'cart': CartForm,
'order': OrderForm
}
queryset = Cart.objects.all()
success_url = reverse_lazy('make_payment')

def get_cart_initial(self):
user = self.request.user.get_full_name
return {'user':user}
# return {'email':'[email protected]', 'user':user,}

def get_order_initial(self):
return {'email':'[email protected]'}

def get_context_data(self, **kwargs):
context = super(OrderFormView, self).get_context_data(**kwargs)
context.update({"menu":Menu.objects.all(), "cart": self.queryset})
return context

def post(self, request):

form = OrderForm(request.POST)

if form.is_valid():

form.save()

return redirect('make_payment')

return render(request, self.template_name, context={'form':form,})

def cart_form_valid(self, form):
form.save(self.request)
# cart.menu = self.request.POST['menu']
# cart.qty = self.request.POST['qty']
# cart.price = Menu.objects.filter(id(self.request.POST['pk'])).price
# return redirect('order_view')
# return form(self.request, cart, redirect_url=self.get_success_url())

def order_form_valid(self, form):
order = form.save(self.request)
return redirect('make_payment')
# return form.signup(self.request, order, self.get_success_url())

@OtterWebDev
Copy link

Thank you so much for this!! But can someone help me with the "grouping" part, cause i'm not able to send like 2 forms, only one or all

@caiopaje
Copy link

I am getting the following error, how can I resolve it?
image

@leogenius360
Copy link

@caiopaje could you add a screenshot of your view?
From the error, you didn't exposed the field "atividade" field from your forms or you don't have such field in your model "NovaAtividade"

@JJDabrowski
Copy link

Hi guys, could you help me with a trial to use your approach for my needs?

https://stackoverflow.com/questions/76357340/cannot-proceed-with-multiform-view-in-class-based-views

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment