Django Inline Formset Factory with Examples.

For a recent project, I had to use Django inline formset factory using class-based views. You can update a number of identical forms on the same page using a Django inline formset factory. In essence, it enables you to bulk edit a number of things at once.

I saw some articles. They are using some external javascript packages for this work. Here I am using custom JS. And it's super easy.

First of all, why we need Django inline formset:

To allow a user to create and update related via Foreign Key objects from the create/update views of a referenced object, all on one page.

Scenario: Add Images and Variants to Product.

Suppose we are working on an e-commerce website and we have a Product model. As we know that one product may have many images and variants and we don’t know how many images or variants users will choose for one product. So we want a user to be able to add as many images and variants, as they need simply by pressing the "add more" button, which creates a new row in Add product form.

What will we do in this article?

1) List all products.

2) Create new products with images and variants.

3) Update products with images and variants.

4) Delete formsets using django builtin delete checkboxes. And also custom coded delete button functionality.

Here is what the end result looks like:

1) List all products.

2) Create new products with images and variants.

3) Update the product with images and variants.

What is Inline Formset Factory?

Inline formsets is a small abstraction layer on top of model formsets. These simplify the case of working with related objects via a foreign key.

function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None, min_num=None, validate_min=False, field_classes=None, absolute_max=None, can_delete_extra=True, renderer=None, edit_only=False)

In Django 3.2 version two new arguments added:

  1. absolute_max: Limiting the maximum number of instantiated forms. The absolute_max parameter allows limiting the number of forms that can be instantiated when supplying POST data. This protects against memory exhaustion attacks using forged POST requests
  2. can_delete_extra (The new can_delete_extra argument allows the removal of the option to delete extra forms)

In Django 4.1 one new argument is added:

  1. edit_only (The new edit_only argument for inlineformset_factory allows preventing new objects creation.)

Inline Formset function parameters detail:

parent_model*: Parent model is your main model. In our example Product is the parent_model. It is required field.

model*: Model which has foriegn key to the parent model. In our example Image and Variant are model.

form*: Define the Model form. In our case ImageForm and VariantForm are form.

fromset: You can provide model formset by overriding BaseInlineFormSet, by default BaseInlineFormSet is provided by django.

fk_name: If your model has more than one: class:`~django.db.models.ForeignKey` to the parent_model, you must specify a fk_name.

fields: is an optional list of field names. If provided, only the named fields will be included in the returned fields.

exclude: is an optional list of field names. If provided, the named fields will be excluded from the returned fields, even if they are listed in the fields argument.

extra: Number of extra inline forms you want to show. By default it is 3.

can_order: to help with ordering of forms in formsets. By default False.

can_delete: to help with the deletion of forms in formsets. By default True.

max_num: gives you the ability to limit the number of forms the formset will display

formfield_callback: is a callable that takes a model field and returns a form field. This method will run before converting a model field into a form field.

widgets: is a dictionary of model field names mapped to a widget. You can add widgets for form field customization.

validate_max: If validate_max=True is passed then validation will also check that the number of forms in the data set, minus those marked for deletion is less than or equal to max_num. It validates against max_num strictly even if max_num was exceeded because the amount of initial data supplied was excessive.

localized_fields: is a list of names of fields that should be localized just like a model form meta option. It adds a formset field with support for different languages. 

labels: is a dictionary of model field names mapped to a label. Used for overriding formset fields labels just like model form meta option.

help_texts: is a dictionary of model field names mapped to a help text. Used for adding help text to formset fields just like model form meta option.

error_messages: is a dictionary of model field names mapped to a dictionary of error messages. Used for adding error messages to formset fields.

min_num: gives you the ability to limit the minimum number of forms the formset will display.

validate_min: If ``validate_min=True`` is passed then validation will also check that the number of forms in the data set, minus those marked for deletion, is greater than or equal to min_num.

Related article about min_num and validate_min: How to set at least one inline form required in django inline formset

field_classes: is a dictionary of model field names mapped to a form field class.

absolute_max: Limiting the maximum number of instantiated forms. The absolute_max parameter allows limiting the number of forms that can be instantiated when supplying POST data. This protects against memory exhaustion attacks using forged POST requests

can_delete_extra: The new can_delete_extra argument allows the removal of the option to delete extra forms. While setting can_delete=True, specifying can_delete_extra=False will remove the option to delete extra forms.

edit_only: The new edit_only argument for inlineformset_factory allows preventing new objects creation.

Let's move toward coding.

Requirements.
django==3.2.15
pillow
Models.

Here we have 3 simple models.

# models.py

from django.db import models


class Product(models.Model):
    title = models.CharField(max_length=150)
    short_description = models.TextField(max_length=100)

    def __str__(self):
        return self.title


class Image(models.Model):
    product = models.ForeignKey(
        Product, on_delete=models.CASCADE, null=True
        )
    image = models.ImageField(blank=True, upload_to='images')

    def __str__(self):
        return self.product.title


class Variant(models.Model):
    product = models.ForeignKey(
        Product, on_delete=models.CASCADE
        )
    size = models.CharField(max_length=100)
    quantity = models.PositiveIntegerField(default=1)
    price = models.DecimalField(max_digits=12, decimal_places=2)

    def __str__(self):
        return self.product.title
Forms.
# forms.py

from django import forms
from django.forms import inlineformset_factory

from .models import (
    Product, Image, Variant
)


class ProductForm(forms.ModelForm):

    class Meta:
        model = Product
        fields = '__all__'
        widgets = {
            'title': forms.TextInput(
                attrs={
                    'class': 'form-control'
                    }
                ),
            'short_description': forms.TextInput(
                attrs={
                    'class': 'form-control'
                    }
                ),
        }


class ImageForm(forms.ModelForm):

    class Meta:
        model = Image
        fields = '__all__'


class VariantForm(forms.ModelForm):

    class Meta:
        model = Variant
        fields = '__all__'
        widgets = {
            'size': forms.TextInput(
                attrs={
                    'class': 'form-control'
                    }
                ),
            'quantity': forms.NumberInput(
                attrs={
                    'class': 'form-control'
                    }
                ),
            'price': forms.NumberInput(
                attrs={
                    'class': 'form-control'
                    }
                ),
        }


VariantFormSet = inlineformset_factory(
    Product, Variant, form=VariantForm,
    extra=1, can_delete=True, can_delete_extra=True
)
ImageFormSet = inlineformset_factory(
    Product, Image, form=ImageForm,
    extra=1, can_delete=True, can_delete_extra=True
)

In this article,  I am using django formset builtin delete by specifying can_delete=True and can_delete_extra=True in inlineformse_factory function and also I am building the custom-coded delete button.

Views.

Importing modules

# views.py

from django.shortcuts import render, redirect
from django.contrib import messages
from django.views.generic import ListView
from django.views.generic.edit import (
    CreateView, UpdateView
)

from .forms import (
    ProductForm, VariantFormSet, ImageFormSet
)
from .models import (
    Image,
    Product,
    Variant
)

In this article, we are following the DRY (don't repeat yourself) principle. So that's why I am creating a parent class ProductInline. And our ProductCreate and ProductUpdate classes inherit it.

# views.py

class ProductInline():
    form_class = ProductForm
    model = Product
    template_name = "products/product_create_or_update.html"

    def form_valid(self, form):
        named_formsets = self.get_named_formsets()
        if not all((x.is_valid() for x in named_formsets.values())):
            return self.render_to_response(self.get_context_data(form=form))

        self.object = form.save()

        # for every formset, attempt to find a specific formset save function
        # otherwise, just save.
        for name, formset in named_formsets.items():
            formset_save_func = getattr(self, 'formset_{0}_valid'.format(name), None)
            if formset_save_func is not None:
                formset_save_func(formset)
            else:
                formset.save()
        return redirect('products:list_products')

    def formset_variants_valid(self, formset):
        """
        Hook for custom formset saving.Useful if you have multiple formsets
        """
        variants = formset.save(commit=False)  # self.save_formset(formset, contact)
        # add this 2 lines, if you have can_delete=True parameter 
        # set in inlineformset_factory func
        for obj in formset.deleted_objects:
            obj.delete()
        for variant in variants:
            variant.product = self.object
            variant.save()

    def formset_images_valid(self, formset):
        """
        Hook for custom formset saving. Useful if you have multiple formsets
        """
        images = formset.save(commit=False)  # self.save_formset(formset, contact)
        # add this 2 lines, if you have can_delete=True parameter 
        # set in inlineformset_factory func
        for obj in formset.deleted_objects:
            obj.delete()
        for image in images:
            image.product = self.object
            image.save()

We have two inline forms. 1) for images 2) for variants. You need to specify a function for each inline formsets and we did it in the above code snippet. like

def formset_variants_valid(self, formset):

def formset_images_valid(self, formset):

Now add ProductCreate and ProductUpdate classes.

# views.py

class ProductCreate(ProductInline, CreateView):

    def get_context_data(self, **kwargs):
        ctx = super(ProductCreate, self).get_context_data(**kwargs)
        ctx['named_formsets'] = self.get_named_formsets()
        return ctx

    def get_named_formsets(self):
        if self.request.method == "GET":
            return {
                'variants': VariantFormSet(prefix='variants'),
                'images': ImageFormSet(prefix='images'),
            }
        else:
            return {
                'variants': VariantFormSet(self.request.POST or None, self.request.FILES or None, prefix='variants'),
                'images': ImageFormSet(self.request.POST or None, self.request.FILES or None, prefix='images'),
            }


class ProductUpdate(ProductInline, UpdateView):

    def get_context_data(self, **kwargs):
        ctx = super(ProductUpdate, self).get_context_data(**kwargs)
        ctx['named_formsets'] = self.get_named_formsets()
        return ctx

    def get_named_formsets(self):
        return {
            'variants': VariantFormSet(self.request.POST or None, self.request.FILES or None, instance=self.object, prefix='variants'),
            'images': ImageFormSet(self.request.POST or None, self.request.FILES or None, instance=self.object, prefix='images'),
        }

get_named_formsets functions in both classes are very easy to understand.

Now we are adding a view for listing the Products.

# views.py

class ProductList(ListView):
    model = Product
    template_name = "products/product_list.html"
    context_object_name = "products"

Now add functions for deleting images and variants.

# views.py

'''This 2 functions are for custom added delete button functionality. If you don't want to use custom delete buttons than don't add this'''


def delete_image(request, pk):
    try:
        image = Image.objects.get(id=pk)
    except Image.DoesNotExist:
        messages.success(
            request, 'Object Does not exit'
            )
        return redirect('products:update_product', pk=image.product.id)

    image.delete()
    messages.success(
            request, 'Image deleted successfully'
            )
    return redirect('products:update_product', pk=image.product.id)


def delete_variant(request, pk):
    try:
        variant = Variant.objects.get(id=pk)
    except Variant.DoesNotExist:
        messages.success(
            request, 'Object Does not exit'
            )
        return redirect('products:update_product', pk=variant.product.id)

    variant.delete()
    messages.success(
            request, 'Variant deleted successfully'
            )
    return redirect('products:update_product', pk=variant.product.id)
Urls.
# products/urls.py

from django.urls import path

from .views import (
    ProductList, ProductCreate, ProductUpdate,
    delete_image, delete_variant
)

app_name = 'products'

urlpatterns = [
    path('products/', ProductList.as_view(), name='list_products'),
    path('create/', ProductCreate.as_view(), name='create_product'),
    path('update/<int:pk>/', ProductUpdate.as_view(), name='update_product'),
    path('delete-image/<int:pk>/', delete_image, name='delete_image'),
    path('delete-variant/<int:pk>/', delete_variant, name='delete_variant'),
]
Templates.
<!-- product_list.html -->
{% extends "base.html" %}


{% block content %}
    <div class="card">
        <div class="card-header card-header-secondary">
            <h4 class="card-title">Products</h4>
            <a href="{% url 'products:create_product' %}">Add more products</a>
        </div>
        <ul>
            {% for product in products %}
                <li class="card-body">{{ product.title }} | <a href="{% url 'products:update_product' product.id %}">Edit</a></li>
            {% endfor %}
        </ul>
    </div>
{% endblock content %}

Now add product_create_or_update.html

Note: Please read all comments added to this file (product_create_or_update.html). Because all comments are very important to read and understand.

<!-- product_create_or_update.html -->

{% extends "base.html" %}

{% block content %}
<form enctype="multipart/form-data" class="container" method="post" id="product_form">
    {% csrf_token %}
    <!-- main form start --- in our case product form -->
    <div class="card">
        <div class="card-header card-header-secondary">
            <h4 class="card-title">Add Products</h4>
        </div>
        {% for field in form %}
            <div class="form-group card-body">
                <label>{{field.label}}</label>
                {% if field.field.required %}
                    <span style="color: red;" class="required">*</span>
                {% endif %}
            
                {{field}}
                {% if field.help_text %}
                    <small style="color: grey">{{ field.help_text }}</small>
                {% endif %}
                {% for error in field.errors %}
                    <p style="color: red">{{ error }}</p>
                {% endfor %}
            </div>
        {% endfor %}
    </div>
    <!-- main form end --- in our case product form -->

    <!-- inline form for Images start -->

    <!-- EXPLAINING with named_formsets.images as formset -->
    <!-- Note: named_formsets is used in get_context_data function in views.py -->
    <!-- Note: here images is our ImageFormSet name, used in get_named_formsets function in views.py -->
    {% with named_formsets.images as formset %}  
        {{ formset.management_form }}
        <script type="text/html" id="images-template">   // id="inlineformsetname-template"
            <tr id="images-__prefix__" class= hide_all>  // id="inlineformsetname-__prefix__"
                {% for fields in formset.empty_form.hidden_fields %}
                    {{ fields }}
                {% endfor %}
            
                {% for fields in formset.empty_form.visible_fields %}
                    <td>{{fields}}</td>
                {% endfor %}
            </tr>
        </script>

        <div class="table-responsive card mt-4">
            <div class="card-header card-header-secondary">
                <h4 class="card-title">Add Images</h4>
            </div>
            <table class="table card-body">
                <thead class="text-secondary">
                    <th>Image <span style="color: red;" class="required">*</span></th>
                    <th>Delete?</th>
                    <th>Custom Delete btn</th>
                </thead>
                <tbody id="item-images">  <!-- id="item-inlineformsetname" -->
                    <!-- formset non forms errors -->
                    {% for error in formset.non_form_errors %}
                        <span style="color: red">{{ error }}</span>
                    {% endfor %}
                    {% for formss in formset %}
                        {{ formss.management_form }}
                        <tr id="images-{{ forloop.counter0 }}" class= hide_all>  <!-- id="inlineformsetname-counter" -->
                            {{ formss.id }}
                            {% for field in formss.visible_fields %}
                                <td>
                                    {{field}}
                                    {% for error in field.errors %}
                                        <span style="color: red">{{ error }}</span>
                                    {% endfor %}
                                </td>
                            {% endfor %}
                            <!-- delete code -->
                            {% if formss.instance.pk %}
                                <td>
                                    <button type="button" class="btn btn-danger" data-toggle="modal" data-target="#exampleModal{{formss.instance.pk}}">
                                        Delete
                                    </button>
                                    <!-- Modal -->
                                    <div class="modal fade" id="exampleModal{{formss.instance.pk}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel{{formss.instance.pk}}" aria-hidden="true">
                                        <div class="modal-dialog" role="document">
                                        <div class="modal-content">
                                            <div class="modal-header">
                                            <h5 class="modal-title" id="exampleModalLabel{{formss.instance.pk}}">Are Your Sure You Want To Delete This?</h5>
                                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                                <span aria-hidden="true">&times;</span>
                                            </button>
                                            </div>
                                            <div class="modal-footer">
                                                <a href="{% url 'products:delete_image' formss.instance.pk %}" type="button" class="btn btn-primary">Yes, Delete</a>
                                                <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                                            </div>
                                        </div>
                                        </div>
                                    </div>
                                </td>            
                            {% endif %}
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
            <a href="#" id="add-image-button" class="btn btn-secondary add-images">Add More</a> <!-- id="add-inlineformsetname-button" -->
        </div>
    {% endwith %}
    <!-- inline form for Images end -->

    <!-- inline form for Variant start -->

    <!-- EXPLAINING with named_formsets.variants as formset -->
    <!-- Note: named_formsets is used in get_context_data function in views.py -->
    <!-- Note: here variants is our VariantFormSet name, used in get_named_formsets function in views.py -->
    {% with named_formsets.variants as formset %}
        {{ formset.management_form }}
        <script type="text/html" id="variants-template">  // id="inlineformsetname-template"
            // id='inlineformsetname-__prefix__' 
            <tr id="variants-__prefix__" class= hide_all>
                {% for fields in formset.empty_form.hidden_fields %}
                    {{ fields }}
                {% endfor %}
            
                {% for fields in formset.empty_form.visible_fields %}
                    <td>{{fields}}</td>
                {% endfor %}
            </tr>
        </script>
        <div class="table-responsive card mt-4">
            <div class="card-header card-header-secondary">
                <h4 class="card-title">Add Variants</h4>
            </div>
            <table class="table card-header">
                <thead class="text-secondary">
                    <th>Size <span style="color: red;" class="required">*</span></th>
                    <th>Quantity <span style="color: red;" class="required">*</span></th>
                    <th>Price <span style="color: red;" class="required">*</span></th>
                    <th>Delete?</th>
                    <th>Custom Delete btn</th>
                </thead>
                <tbody id="item-variants">  <!-- id="item-inlineformsetname" -->
                    <!-- formset non forms errors -->
                    {% for error in formset.non_form_errors %}
                        <span style="color: red">{{ error }}</span>
                    {% endfor %}
                    {% for formss in formset %}
                        {{ formss.management_form }}
                        <tr id="variants-{{ forloop.counter0 }}" class= hide_all>  <!-- id="inlineformsetname-counter" -->
                            {{ formss.id }}
                            {% for field in formss.visible_fields %}
                                <td>
                                    {{field}}
                                    {% for error in field.errors %}
                                        <span style="color: red">{{ error }}</span>
                                    {% endfor %}
                                    
                                    {% comment %} {{ field.DELETE }} {% endcomment %}
                                </td>
                            {% endfor %}
                            {% comment %} for delete {% endcomment %}
                            {% if formss.instance.pk %}
                                <td>
                                    <button type="button" class="btn btn-danger" data-toggle="modal" data-target="#exampleModal{{formss.instance.pk}}">
                                        Delete
                                    </button>
                                    <!-- Modal -->
                                    <div class="modal fade" id="exampleModal{{formss.instance.pk}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel{{formss.instance.pk}}" aria-hidden="true">
                                        <div class="modal-dialog" role="document">
                                        <div class="modal-content">
                                            <div class="modal-header">
                                            <h5 class="modal-title" id="exampleModalLabel{{formss.instance.pk}}">Are Your Sure You Want To Delete This?</h5>
                                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                                <span aria-hidden="true">&times;</span>
                                            </button>
                                            </div>
                                            <div class="modal-footer">
                                                <a href="{% url 'products:delete_variant' formss.instance.pk %}" type="button" class="btn btn-primary">Yes, Delete</a>
                                                <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                                            </div>
                                        </div>
                                        </div>
                                    </div>
                                </td>            
                            {% endif %}
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
            <a href="#" id="add-variant-button" class="btn btn-secondary add-variants">Add More</a> <!-- id="add-inlineformsetname-button" -->
        </div>
        
        {% endwith %}
        <!-- inline form for Images end -->

        <div class="form-group">
            <button type="submit" class="btn btn-secondary btn-block">Submit</button>
        </div>
</form>

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>

<script>
    $(document).ready(function() {
        // when user clicks add more btn of images
      $('.add-images').click(function(ev) {
          ev.preventDefault();
          var count = $('#item-images').children().length;
          var tmplMarkup = $('#images-template').html();
          var compiledTmpl = tmplMarkup.replace(/__prefix__/g, count);
          $('#item-images').append(compiledTmpl);
  
          // update form count
          $('#id_images-TOTAL_FORMS').attr('value', count+1);
      });
  });

  $(document).ready(function() {
    // when user clicks add more btn of variants
      $('.add-variants').click(function(ev) {
          ev.preventDefault();
          var count = $('#item-variants').children().length;
          var tmplMarkup = $('#variants-template').html();
          var compiledTmpl = tmplMarkup.replace(/__prefix__/g, count);
          $('#item-variants').append(compiledTmpl);
  
          // update form count
          $('#id_variants-TOTAL_FORMS').attr('value', count+1);
      });
  });
</script>
        
{% endblock content %}

That’s it for now. Hope you find it useful. Don't forget to press the clap button if you like it and you can also show support by buying me a coffee. If you have any questions or facing any issues then feel free to comment in the comment section. And for more upcoming articles don't forget to subscribe to our newsletter.

Source code can be found here.

Thank You.

Happy coding.

Source:

Documentation

Related Article.

How to set at least one inline form required in django inline formset

Django Inline Admin with examples

I would greatly appreciate it if you subscribe to my YouTube channel, Let's Code More

 ⋅  31 comments have been posted.
    Feb. 12, 2023, 4:15 p.m. - Areeba Seher moderator  
    Hello everyone, there have been many inquiries about this blog theme. I purchased it from the Envato market for a reasonable price of 19$. If you're interested in purchasing this theme for your blog, you can find it here: https://1.envato.market/e4yPvr
    Reply
    March 25, 2024, 8:17 a.m. - Layla  
    Hello Areeba, thanks for this great article! I am an absolute begginer and just learning Django, so this helped me alot. One question though, I want to remove the toggle delete button from the variants table, but I can't seem to fine the exact part of the code that deploys this button, all I want to do is to keep the custom button and remove the toggle button if that makes sense. Can I please get your kind guidance on this?
    Reply
    Dec. 8, 2022, 5:01 a.m. - Pavel Kovtun  
    Thanks for this! I have one problem with that code, it was not possible to implement the functionality of updating the model, because the instance=self.object is always None, I don’t understand what I did wrong, but setting an self.object in a mixin does not forward this attribute to child classes
    Reply
    Dec. 8, 2022, 6:38 a.m. - Pavel Kovtun  
    Oh... i spend 6 hours for this The problem was that I was using CreateView instead of UpdateView...
    Reply
    Dec. 8, 2022, 7:32 a.m. - Areeba Seher  
    I am glad that you have solved your problem :)
    Dec. 8, 2022, 1:39 a.m. - Dcr  
    Thanks for this, very very helpful. On the ProductUpdate view, what if i want to apply filter so that the items displayed on the formset are based not only the 'pk' value but also on another condition. For example, a unique string used as a reference number. So that the url path would look like: path('update/<int:pk>/<str:ref_number>', ProductUpdate.as_view(), name='update_product') I added <str:ref_number>, which allows me to filter all items that contain that ref_number, not just filter by the 'pk'. What should the value 'instance' be in the VariantFormSet below: 'variants': VariantFormSet(self.request.POST or None, self.request.FILES or None, instance=???, prefix='variants') ?
    Reply
    Dec. 8, 2022, 2:23 a.m. - Dcr  
    I figured it out, i had to add a queryset parameter inside the formset. It worked.
    Reply
    Dec. 8, 2022, 7:33 a.m. - Areeba Seher  
    That’s great :)
    Jan. 11, 2023, 1:05 a.m. - Fuh  
    Please, any idea of how to add a queryset parameter inside the formset? I keep getting this error "UpdateView is missing a QuerySet" and can't seem to solve it irrespective of all what i have done. Thanks
    Jan. 11, 2023, 6:29 a.m. - Areeba Seher  
    Hi Fu, It’s very difficult to figure out the problem without seeing your code. Can you please share github link of the code. Please make sure that you are using model = “Your model” in your class. https://stackoverflow.com/questions/43046218/updateview-is-missing-a-queryset-error
    Jan. 11, 2023, 6:44 a.m. - Areeba Seher moderator  
    May be this can help you. https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#overriding-methods-on-an-inlineformset
    Jan. 11, 2023, 11:50 a.m. - Fuh  
    Thanks for your quick response, I have tried out the links that you shared but still no luck. I have just extracted the necessary models, forms and views to github ( https://github.com/Fuh2020/updateview.git ). My Create and Update views are adapted directly from your example but still no luck. Thanks a million for your help.
    Jan. 11, 2023, 1:08 p.m. - Areeba Seher moderator  
    I saw your code and added your code to my code and it's working. Please check your Django version. I am not sure why this error is coming. :(
    Jan. 11, 2023, 2:38 p.m. - Fuh  
    I am using Django 4.1 and i am sure this is the problem. Please, lastly any idea of how i can add a queryset parameter inside the formset?? Thanks for your time
    Jan. 11, 2023, 4:54 p.m. - Fuh  
    Thanks so much for your time, I figured out another way with django-extra-views
    Dec. 4, 2022, 11:48 a.m. - ken  
    I have a project with the same idea but i would like to use Function-based view can you please share code
    Reply
    Dec. 4, 2022, 2:08 p.m. - ken  
    i have link you blog to stackoverflow https://stackoverflow.com/q/74674591/15720655 if its okay with you
    Reply
    Dec. 4, 2022, 2:33 p.m. - Areeba Seher  
    I dont know how to do same work with function based views. I think you should check documentation. And no worries you can post it on stackoverflow.
    Nov. 7, 2022, 7:42 a.m. - developer  
    Thankyou , You are doing great job dear .
    Reply
    Nov. 7, 2022, 1:46 p.m. - Areeba Seher moderator  
    You're welcome. Subscribe to our newsletter for upcoming articles.
    Reply
    Oct. 16, 2022, 5:34 p.m. - Kerireme  
    Thorough explanation. Can you post or send me the complete code of product_list.html and the code of function to delete product?, thankyou so much.
    Reply
    Oct. 16, 2022, 6:06 p.m. - Areeba Seher  
    You’re welcome Complete Source code is available here: https://github.com/LetsCodeMore/Django-Inline-Formset-Tutorials
    Reply
    Oct. 11, 2022, 8:29 p.m. - Abrar Ahmed  
    Nice and thorough explanation, thankyou areeba
    Reply
    Oct. 12, 2022, 2:15 a.m. - Areeba Seher  
    You’re welcome Abrar. Don’t forget to subscribe our newsletter for upcoming articles. Happy coding.
    Reply
    Sept. 14, 2022, 11:25 p.m. - Fulani  
    This very important Thank for shared
    Reply
    Sept. 15, 2022, 5:11 p.m. - Areeba moderator  
    You're welcome Fulani. Don't forget to subscribe to our newsletter for more articles.
    Reply
    Feb. 21, 2023, 1:02 p.m. - Liz  
    Very nice work! I am trying something similar but my 'Variant' model has m2m fields on it and the form doesn't seem to save anything to those when submitting, any ideas? Thanks!
    Feb. 21, 2023, 1:10 p.m. - Liz  
    I've answered my own question! But for anyone else: see: https://stackoverflow.com/questions/5612991/saving-many-to-many-data-via-a-modelform-in-django You just have to add formset.save_m2m() to the end of the formset_variants_valid method def formset_variants_valid(self, formset): """ Hook for custom formset saving. Useful if you have multiple formsets """ variants = formset.save(commit=False) # self.save_formset(formset, contact) # add this 2 lines, if you have can_delete=True parameter # set in inlineformset_factory func for obj in formset.deleted_objects: obj.delete() for variant in variants: variant.neaq_no = self.object variant.save() formset.save_m2m()
    Feb. 21, 2023, 3:20 p.m. - Areeba Seher moderator  
    Thank you Liz for helping others.
    Aug. 13, 2023, 9:54 p.m. - Ahmed  
    Thanks , How i can change the style in the script below as i am looking to style it by field ( for example 2 field in one row , not as_table and not as_P) sorry for my english not good enough but i hope you are understand me and advise me soon with thanks . <script type="text/html" id="images-template"> // id="inlineformsetname-template" <tr id="images-__prefix__" class= hide_all> // id="inlineformsetname-__prefix__" {% for fields in formset.empty_form.hidden_fields %} {{ fields. }} {% endfor %} {% for fields in formset.empty_form.visible_fields %} <td>{{fields}}</td> {% endfor %} </tr> </script>
    Sept. 1, 2023, 1:51 p.m. - Areeba Seher moderator  
    Just change the HTML tags and keep the classes same.
    Reply
claps