A CBV to handle multiple forms in one view

Overview

django-shapeshifter

A common problem in Django is how to have a view, especially a class-based view that can display and process multiple forms at once. django-shapeshifter aims to make this problem much more trivial.

Right now, django-shapeshifter can handle any (well, theoretically) number of forms in a single view. A view class is provided for multiple standard forms or model forms. To mix and match these form types, you'll need to do a little extra work. Here's how to use the package:

Installation

$ pip install django-shapeshifter

You should not need to add shapeshifter to your INSTALLED_APPS.

Usage

You use django-shapeshifter just like you use Django's built-in class-based views. You should be able to use the provided views with most mixins you're already using in your project, such as LoginRequiredMixin. Certain mixins may have to be refactored, such as SuccessMessageMixin, which is trigged on the form_valid() method.

Let's look at using the view with a few standard forms:

interests/views.py

from django.urls import reverse_lazy

from shapeshifter.views import MultiFormView

from . import forms


class InterestFormsView(MultiFormView):
    form_classes = (forms.ContactForm, forms.InterestsForm, forms.GDPRForm)
    template_name = 'interests/forms.html'
    success_url = reverse_lazy('interests:thanks')

But what do you need to do in the template? The view's context will contain a new member, forms, that you can iterate over to display each form:

interests/templates/interests/forms.html

{% extends 'layout.html' %}

{% block content %}
<h3>Please fill out your interests below!</h3>

<form method="POST">
{% csrf_token %}
{% for form in forms %}
    {{ form.as_p }}
{% endfor %}
    <input type="submit" value="Save" />
</form>
{% endblock content %}

This will generate a template with all three forms, in succession, inside of a single <form> tag. All of the forms must be submitted together. After submission, Django will fill each form in with the appropriate submitted data, validate them, and then redirect to your success_url.

But with just the above code, nothing will happen with the form data. To control that, you need to override the forms_valid method in your view. Here's what that might look like:

interests/views.py

class InterestsFormView(MultiFormView):
   ...
   def forms_valid(self):
       forms = self.get_forms()
       contact_form = forms['contactform']
       interest_form = forms['interestsform']
       gdpr = forms['gdprform']
       
       if not gdpr.data['accept']:
           messages.error("You must accept the GDPR terms.")
           return HttpResponseRedirect(reverse_lazy('interests:forms'))
       salesforce_client.send(zip(contact_form.data.items(),
                                  interest_form.data.items()))
       return super().forms_valid()

The above code isn't meant to be a complete example but should give you an idea of what would be done to handle the form data.

What about model forms?

All of the above code is valid for model forms, too, with one exception. For model forms, instead of extending MultiFormView, you'll extend MultiModelFormView. There are two major differences between the classes but the most important one is that forms_valid will call form.save() on each form. Here is an example allowing a user to edit their User first_name and last_name, and their first Profile name on one form:

my_app/models.py

from django.contrib.auth.models import User

class Profile(models.Model):
    name = models.CharField(max_length=255)
    user = models.ForeignKey(User, related_name='profiles', on_delete=models.CASCADE)

my_app/forms.py

from django.contrib.auth.models import User

from .models import Profile

class UserForm(forms.ModelForm):
    class Meta:
        model = User

        fields = [
            'first_name',
            'last_name',
        ]

class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = [
            'name',
        ]

        labels = {
            'name': 'Profile Name',
        }

my_app/views.py

from shapeshifter.views import MultiModelFormView
from shapeshifter.mixins import MultiSuccessMessageMixin

from .forms import UserForm, ProfileForm

class UserUpdateView(LoginRequiredMixin, MultiSuccessMessageMixin, MultiModelFormView):
    form_classes = (UserForm, ProfileForm)
    template_name = 'my_app/forms.html'
    success_url = reverse_lazy('home')
    success_message = 'Your profile has been updated.'

    def get_instances(self):
        instances = {
            'userform': self.request.user,
            'profileform': profile_instance = Profile.objects.filter(
                user=self.request.user,
            ).first(),
        }

        return instances

What if I want to mix model and standard form?

That's fine! You will have to override forms_valid in your view to handle the processing of each form but everything else should work exactly the same.

API

MultiFormView (and MultiModelFormView by inheritance) extends Django's TemplateView. Additionally it adds a few methods for the instantiation and processing of the forms. Any and all of these can be overwritten to customize the behavior of your views.

Below is each attribute and their default value, and each method with its signature and return value.

Attributes

  • initial = {} - Initial values for each form. Should be a dict formatted with the following format:
initial = {
    'contactform': {
        'name': 'Katherine Johnson'
    }
}

where ContactForm is the class name of the form you're providing initial values for.

  • form_classes = None - a list or tuple of Form (or ModelForm if using MultiModelFormView) classes. Do not instantiate the class, just provide the name).

  • success_url = None - the URL to redirect users to once the forms are all filled in correctly. This can be a URL or a reverse_lazy instance.

Methods

  • get_form_classes(self) - Returns the view's form_classes attribute. Override this method if you need to dynamically set the forms that should be included in the view.

  • get_forms(self) -> dict - Instantiates each form, using the kwargs from get_form_kwargs and returns them all as a dict with the key being a standardized version of the form's class name. Override this if you need to change how the forms are instantiated.

  • get_form_class_name(self, form_class) -> str - Converts the form's class name into a lowercase string. ContactForm will become contactform. You can override this to provide for a different standardized name for your forms.

  • get_form_kwargs(self, form_class) -> dict - Returns a dict of keyword arguments for the form's creation. Prefixes each form with the lowercased class name, provides any initial arguments for the form, and, if the view was requested as either POST or PUT, provides both data and files to the form. For MultiModelFormView, this method also provides the instance for the form. Override this method to add or change the form kwargs.

  • validate_forms(self) -> bool - Calls form.is_valid() for each form and returns the result for the entire set of forms. Override this method if your forms require any special validation steps.

  • forms_valid(self) - This method is called if all forms pass validation. In MultiFormView, this method simply redirects to the success_url. For the model-based version, MultiModelFormView, this method calls form.save() on each form and then redirects. Override this method to change what happens when the forms are all valid.

  • forms_invalid(self) - If any of the forms fail their validation check, this method is executed. By default, it re-renders the view, presenting the forms with their errors. You can override this method if you need something else to happen when not all forms are valid.

MultiModelFormView's extra attributes and methods

As mentioned above, a few things are handled differently in MultiModelFormView.

  • instances = {} - This attribute should be a dict with lowercase form class names as keys. The values should be the instance to use for the form.

  • get_instances(self) - Returns the value of instances. Override this if you need to dynamically fetch the instances for the forms.

MultiSuccessMessageMixin attributes and methods

  • success_message = None - A string containing a success message to add through Django's messages framework.

  • get_success_message(self) - A method which returns the success message. Defaults to self.success_message.

  • forms_valid(self) - Returns the response after adding the success message.

Contributing

Thank you for your interest, time, and energy! Contributions are always welcome and will be reviewed as quickly as possible (that said, we're all volunteers with other jobs/responsibilities so it might be awhile).

Please fork this repository and make your changes in the shapeshifter package. Be sure to add a test for any functionality changes. Once all tests pass, you can submit a pull request with your changes, the rationale behind them, and any special steps the maintainers will need to take to test your changes or replicate the bug you're fixing. Be sure to include adding your name to the following list of contributors!

Contributors

  • Kenneth Love
  • Lacey Williams Henschel
  • Tim Allen

What's with the name? And the version?

The original name was already taken so a new one had to be found. Since this package deals with multiple forms, shapeshifter was a good pun (shapeshifters can take on many forms).

The version number is based on the date of release.

You might also like...
Store model history and view/revert changes from admin site.

django-simple-history django-simple-history stores Django model state on every create/update/delete. This app supports the following combinations of D

Organize Django settings into multiple files and directories. Easily override and modify settings. Use wildcards and optional settings files.
Organize Django settings into multiple files and directories. Easily override and modify settings. Use wildcards and optional settings files.

Organize Django settings into multiple files and directories. Easily override and modify settings. Use wildcards in settings file paths and mark setti

A better and faster multiple selection widget with suggestions
A better and faster multiple selection widget with suggestions

django-searchable-select A better and faster multiple selection widget with suggestions for Django This project is looking for maintainers! Please ope

Yet another Django audit log app, hopefully the simplest one.

django-easy-audit Yet another Django audit log app, hopefully the easiest one. This app allows you to keep track of every action taken by your users.

A beginner django project and also my first Django project which involves shortening of a longer URL into a short one using a unique id.

Django-URL-Shortener A beginner django project and also my first Django project which involves shortening of a longer URL into a short one using a uni

Django query profiler - one profiler to rule them all.  Shows queries, detects N+1 and gives recommendations on how to resolve them
Django query profiler - one profiler to rule them all. Shows queries, detects N+1 and gives recommendations on how to resolve them

Django Query Profiler This is a query profiler for Django applications, for helping developers answer the question "My Django code/page/API is slow, H

A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for quickly creating new images from the one assigned to the field.

django-versatileimagefield A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for creat

A CBV to handle multiple forms in one view

django-shapeshifter A common problem in Django is how to have a view, especially a class-based view that can display and process multiple forms at onc

This app makes it extremely easy to build Django powered SPA's (Single Page App) or Mobile apps exposing all registration and authentication related functionality as CBV's (Class Base View) and REST (JSON)

Welcome to django-rest-auth Repository is unmaintained at the moment (on pause). More info can be found on this issue page: https://github.com/Tivix/d

The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.
The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.

django-crispy-forms The best way to have Django DRY forms. Build programmatic reusable layouts out of components, having full control of the rendered

The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.
The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.

django-crispy-forms The best way to have Django DRY forms. Build programmatic reusable layouts out of components, having full control of the rendered

The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.
The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.

django-crispy-forms The best way to have Django DRY forms. Build programmatic reusable layouts out of components, having full control of the rendered

Blender add-on: Add to Cameras menu: View → Camera, View → Add Camera, Camera → View, Previous Camera, Next Camera
Blender add-on: Add to Cameras menu: View → Camera, View → Add Camera, Camera → View, Previous Camera, Next Camera

Blender add-on: Camera additions In 3D view, it adds these actions to the View|Cameras menu: View → Camera : set the current camera to the 3D view Vie

(CVPR 2022 - oral) Multi-View Depth Estimation by Fusing Single-View Depth Probability with Multi-View Geometry
(CVPR 2022 - oral) Multi-View Depth Estimation by Fusing Single-View Depth Probability with Multi-View Geometry

Multi-View Depth Estimation by Fusing Single-View Depth Probability with Multi-View Geometry Official implementation of the paper Multi-View Depth Est

Endpoints is a lightweight REST api framework written in python and used in multiple production systems that handle millions of requests daily.

Endpoints Quickest API builder in the West! Endpoints is a lightweight REST api framework written in python and used in multiple production systems th

A simple chat room using socket and threading for handle multiple connections.
A simple chat room using socket and threading for handle multiple connections.

• Socket Chat Room was a little project for socket study. It works with a server handling the incoming connections from the clients. Clients send encoded messages while waiting for others clients messages simultaneously. And the server receive all the messages and delivers to the other clients.

Official PyTorch implementation of MX-Font (Multiple Heads are Better than One: Few-shot Font Generation with Multiple Localized Experts)

Introduction Pytorch implementation of Multiple Heads are Better than One: Few-shot Font Generation with Multiple Localized Expert. | paper Song Park1

One version package to rule them all, One version package to find them, One version package to bring them all, and in the darkness bind them.

AwesomeVersion One version package to rule them all, One version package to find them, One version package to bring them all, and in the darkness bind

[CVPR'21] Projecting Your View Attentively: Monocular Road Scene Layout Estimation via Cross-view Transformation
[CVPR'21] Projecting Your View Attentively: Monocular Road Scene Layout Estimation via Cross-view Transformation

Projecting Your View Attentively: Monocular Road Scene Layout Estimation via Cross-view Transformation Weixiang Yang, Qi Li, Wenxi Liu, Yuanlong Yu, Y

Comments
Releases(18.9.23)
Owner
Kenneth Love
I teach Python, I write code for my employer to get paid and open source to scratch my own itches.
Kenneth Love
Add Chart.js visualizations to your Django admin using a mixin class

django-admincharts Add Chart.js visualizations to your Django admin using a mixin class. Example from django.contrib import admin from .models import

Dropseed 22 Nov 22, 2022
This is a personal django website for forum posts

Django Web Forum This is a personal django website for forum posts It includes login, registration and forum posts with date time. Tech / Framework us

5 May 12, 2022
Vehicle registration using Python, Django and SQlite3

PythonCrud Cadastro de veículos utilizando Python, Django e SQlite3 Para acessar o deploy no Heroku:

Jorge Thiago 4 May 20, 2022
a little task queue for python

a lightweight alternative. huey is: a task queue (2019-04-01: version 2.0 released) written in python (2.7+, 3.4+) clean and simple API redis, sqlite,

Charles Leifer 4.3k Dec 29, 2022
Django Simple Spam Blocker is blocking spam by regular expression.

Django Simple Spam Blocker is blocking spam by regular expression.

Masahiko Okada 23 Nov 29, 2022
📊📈 Serves up Pandas dataframes via the Django REST Framework for use in client-side (i.e. d3.js) visualizations and offline analysis (e.g. Excel)

Django REST Pandas Django REST Framework + pandas = A Model-driven Visualization API Django REST Pandas (DRP) provides a simple way to generate and se

wq framework 1.2k Jan 01, 2023
Projeto onde podes inserir notícias, ver todas as notícias guardas e filtrar por tag. A base de dados usada é o mongoDB.

djangoProject Projeto onde podes inserir notícias, ver todas as notícias guardas e filtrar por tag. A base de dados usada é o mongoDB. packages utiliz

Sofia Rocha 1 Feb 22, 2022
Source files for a free pyRevit toolbar.

pyRoovit (WIP) What is this? PyRoovit is/will be a toolbar for the use with pyRevit built by Gavin Crump (aka Aussie BIM Guru). Having used and taught

Gavin Crump 11 Nov 10, 2022
Improved Django model inheritance with automatic downcasting

Polymorphic Models for Django Django-polymorphic simplifies using inherited models in Django projects. When a query is made at the base model, the inh

1.4k Jan 03, 2023
A reusable Django model field for storing ad-hoc JSON data

jsonfield jsonfield is a reusable model field that allows you to store validated JSON, automatically handling serialization to and from the database.

Ryan P Kilby 1.1k Jan 03, 2023
Django Serverless Cron - Run cron jobs easily in a serverless environment

Django Serverless Cron - Run cron jobs easily in a serverless environment

Paul Onteri 41 Dec 16, 2022
Django models and endpoints for working with large images -- tile serving

Django Large Image Models and endpoints for working with large images in Django -- specifically geared towards geospatial tile serving. DISCLAIMER: th

Resonant GeoData 42 Dec 17, 2022
A Django app that allows visitors to interact with your site as a guest user without requiring registration.

django-guest-user A Django app that allows visitors to interact with your site as a guest user without requiring registration. Largely inspired by dja

Julian Wachholz 21 Dec 17, 2022
Website desenvolvido em Django para gerenciamento e upload de arquivos (.pdf).

Website para Gerenciamento de Arquivos Features Esta é uma aplicação full stack web construída para desenvolver habilidades com o framework Django. O

Alinne Grazielle 8 Sep 22, 2022
The Django Leaflet Admin List package provides an admin list view featured by the map and bounding box filter for the geo-based data of the GeoDjango.

The Django Leaflet Admin List package provides an admin list view featured by the map and bounding box filter for the geo-based data of the GeoDjango. It requires a django-leaflet package.

Vsevolod Novikov 33 Nov 11, 2022
A simple polling app made in Django and Bootstrap

DjangoPolls A Simple Polling app made with Django Instructions Make sure you have Python installed Step 1. Open a terminal Step 2. Paste the given cod

Aditya Priyadarshi 1 Nov 10, 2021
A app for managing lessons with Django

Course Notes A app for managing lessons with Django Some Ideas

Motahhar.Mokfi 6 Jan 28, 2022
GeoDjango provides geospatial extensions to the Django web dev framework

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. All documentation is in the "docs" directo

Paul Smith 20 Sep 20, 2022
This website serves as an online database (hosted via SQLLite) for fictional businesses in the area to store contact information (name, email, phone number, etc.) for fictional customers.

Django-Online-Business-Database-Project this project is still in progress Overview of Website This website serves as an online database (hosted via SQ

1 Oct 30, 2021
Logan is a toolkit for building standalone Django applications

Logan Logan is a toolkit for running standalone Django applications. It provides you with tools to create a CLI runner, manage settings, and the abili

David Cramer 206 Jan 03, 2023