Exploring the concept of nested forms in Django

I have a requirement to implement nested Django forms using the following models:

class Publisher(models.Model):
    name = models.CharField(max_length=256)
    address1 = models.CharField(max_length=256)
    address2 = models.CharField(max_length=256)
    city = models.CharField(max_length=256)

class Author(models.Model):
    publisher = models.ForeignKey(Publisher) 
    name = models.CharField(max_length=256)
    address = models.CharField(max_length=256)

class Book(models.Model):
    author = models.ForeignKey(Author)
    name = models.CharField(max_length=256)
    price = models.FloatField()

forms.py

class PublisherForm(ModelForm):
    class Meta:
        model = Publisher

    def __init__(self, *args, **kwargs):

        super(PublisherForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Publisher Name', 'autofocus':'autofocus'}
        self.fields['address'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Publisher Address '}


class AuthorForm(ModelForm):
    class Meta:
        model = Author
        exclude = ('publisher',)    

    def __init__(self, *args, **kwargs):

        super(AuthorForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Author Name'}
        self.fields['address'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Author Address'}

class BookForm(ModelForm):
    class Meta:
        model = Book
        exclude = ('author',)    

    def __init__(self, *args, **kwargs):

        super(BookForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Book Name'}
        self.fields['price'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Book Price'}

To achieve this functionality, I need to create dynamic forms on the same screen as shown in the UI below.

On the above screen, all three model forms should be displayed together.

1. A publisher may have many authors
2. Each author may have many books

You'll notice from the design that there are two buttons for

1. Add Another Author - Adding Multiple Authors
2. Add Book - Adding multiple books for Author

2. Add Book

By clicking on the Add Book button, a new Book form should be created as shown in the screenshot

1. Add another Author

Clicking on Add another author will display a new Author record where you can add multiple Books for this author by clicking on Add Book

In cases where we have only two models A and B, and if B has a ForeignKey to A, this functionality could be achieved using Django formsets or inline_formsets or model_formsets. However, in our case, we need to

  1. Add nested(multiple) Book forms for Author
  2. Add nested(multiple) Author forms for Publisher

How do we accomplish the above functionality? I've searched extensively but haven't been able to figure it out yet.

Answer №1

To achieve this, you can utilize the functionality of inline formsets in Django. In the view for creating a publisher, you can return formsets for authors and books with different prefixes for each form. Then, you can use JavaScript to dynamically add empty forms for both books and authors.

Below is a simplified example I have provided:

The key is to generate book formsets within templates using dynamic form prefixes associated with the parent author (books_formset_0, books_formset_1, etc.). Upon form submission, iterate through each author to find the respective book formset.

You can download a complete Django project to test this code here.

IMPORTANT: Please note that the following code may not be optimized or adhere to certain standard practices like JS templates or AJAX, but it effectively demonstrates how to address the issue at hand.

template.py:

<script type="text/javascript">
$(function () {
    $('form').delegate('.btn_add_book', 'click', function () {
        // Add new book form logic here
    });

    $('form').delegate('#btn_add_author', 'click', function () {
        // Add new author form logic here
    });
})
</script>
<!-- HTML template for adding publishers -->

forms.py:

AuthorInlineFormSet = inlineformset_factory(Publisher, Author, extra=1, can_delete=False)
BookInlineFormSet = inlineformset_factory(Author, Book, extra=1, can_delete=False)

views.py:

class PublisherCreateView(CreateView):
    model = Publisher

    def form_valid(self, form):
        # Form validation logic here

    def get_context_data(self, **kwargs):
        # Context data retrieval logic here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips on retrieving the status code from a jQuery AJAX request

I am trying to figure out how to retrieve the ajax status code in jQuery. Here is the ajax block I am currently working with: $.ajax{ type: "GET", url: "keyword_mapping.html", data:"ajax=yes&sf="+status_flag, success: callback.success ...

Exploring the world of Django URL routing and views

I'm having trouble understanding why my code isn't functioning as expected. My goal is to retrieve the user_id, convert it into a URL, and then open details.html using that user_id. I acknowledge that this may not be the most effective method to ...

The button fails to trigger the AJAX request

I've developed a form that includes a QR code generator. The QR code is generated as an SVG, and below the code is a download button that should trigger an AJAX call to my PHP script when clicked. However, the download button does not seem to initiate ...

AngularJS - displaying a notification when the array length is empty and customizing the visibility to conceal the default action

I've been working on an Angular project where I display a loading circle that disappears once the content is loaded. This circle is simply a CSS class that I call from my HTML only when the page first loads, like this: Actually, the circle consists o ...

Iterate over a collection of objects to find connections between the starting and ending points, and retrieve the number of occurrences

My challenge involves analyzing an array of objects containing origin and destination data, and the total volume of objects moving between locations. I am specifically looking to compare flow counts between two cities. For example, when the origin is Vanco ...

The Journey of React Components: How Child Components Embrace the State Props of the Past

My understanding of states and props in React seems to be off. I am working on a React, Redux based app. Situation: I have a global SVG component that grabs the viewport dimensions from the app in the componentDidMount method. The default props for the ...

How to boost the value of a property within a MongoDB document with Mongoose

Recently, I've been dealing with an array of objects named "items". This array includes different objects and I need to search for each object in MongoDB using the "baseId" found in the Base Document. Once I locate the matching object, I aim to update ...

The connection was terminated unexpectedly.' - RemoteDisconnected error occurred due to 'Remote end closed the connection without providing a response

Utilizing a 3rd party API service for sending text messages. Everything runs smoothly when around 5000 numbers are sent as payload to the API. However, I've observed that if the payload count exceeds 7000 or more, an error code is returned from the A ...

Node.js allows you to seamlessly upload multiple images from various form fields or input types simultaneously

<form action="/upload-images" method="post" enctype="multipart/form-data"> <input type="file" name="image1" /> <input type="file" name="image2" /> <input type= ...

What are the steps to enable JavaScript before the next webpage finishes loading?

I am looking to create a loading screen that triggers when a Django form is submitted. Here is an example code snippet: forms.py class ExampleForm(forms.Form): example_field = forms.CharField( widget=forms.TextInput(attrs={'class': ...

What is the highest image size that desktop Chrome can accommodate?

Let's delve into a specific scenario amidst the vast possibilities. Assume the following technical specifications: Browser: Chrome Latest (Desktop) Hardware: Windows Standard PC with default Intel Graphics card Ram: 8GB Processor: i7 @ 3.40 GHz The ...

Highcharts' 'this' function yielding duplicate objects

I am struggling with using the tooltip pointFormatter in Highcharts to display this.name just once. However, I am facing an issue where the same object is being returned twice, causing it to appear duplicated in the tooltip. You can view the problem in ac ...

Error in Laravel Mix Vuejs component: Syntax problem detected in <template />

My development environment involves Windows 10 OS and I have created a Laravel 8 project integrated with VueJs. However, when I try to access the file ./components/app.vue, it returns a SyntaxError: Unexpected token (1:0) The content of the app.vue file i ...

Is there a way to incorporate a component into Particle.js?

I attempted to encase the Particle around the component but it's not functioning correctly import React from "react"; import { render } from "react-dom"; import Particles from "./Particles"; import "./index.css" ...

Updating Vue.js asynchronously using JavaScript import

I am facing a challenge with two form components that share a common JS validator. import { validateInput } from './validateInput.js' export default { data () { return { email: '<a href="/cdn-cgi/l/email-protection" class="_ ...

Mapping Longitude and Latitude with TopoJSON and D3

Currently utilizing the UK Geo JSON found at this link to generate a UK SVG Map. The goal is to plot longitude and latitude points onto this map. The GeometryCollection place is being added to the map in the following manner: data.objects.places = { ...

What is the step-by-step process for chaining ajax requests using $q.deffer?

I have a task that requires the browser to make N requests to the server, where the requests must be synchronous and start only after the previous request has completed. One way to achieve this is by writing a function with a for loop and recursively call ...

Using axios to call a web API method from within a ReactJS web worker

Currently, I am utilizing Web Worker alongside ReactJS (Context API schema) and encountering a particular issue. The design of my Web API and Context is outlined below: WebWorker.js export default class WebWorker { constructor(worker) { let code = ...

Encountering a 404 error while trying to use the autolinker

I have been struggling with this issue for the past day and can't seem to solve it on my own. Essentially, I am attempting to use Autolinker.js to automatically convert URLs into clickable hyperlinks in my chat application. However, every time I try ...

Is there a way to retrieve the name associated with a channel/server ID?

As I work on enhancing the console log for my discord bot, one of my objectives is to capture all the messages visible to the bot. Here's what I have done so far: client.on('message', message => { const User = client.users.cache.get(m ...