Tech Incent
Django Django Form

Explained Django inline formset factory with example?

explained-django-inline-formset-factory-with-example

Recently I have completed an eCommerce project. I used Django forms interesting feature formset factory. It’s simple forming but it’s useful. So why useful, why where you need formset factory function.
Simply we are used formset factory form multi-form in a single view. so why we use multi-form in single? You saw there are scenario models related fields or foreign key fields. Now we want to create/update models foreign key fields on one page/the same page.

So I am going to explain django inline formset factory with example…

Explain inlineformset_factory

Inlineformset_factory is the method. to work django related objects via a foreign key. django call it small abstraction layout on the top of models formset

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)

So inline formset requirements parent_model as first param, model as the second param, and form param value as ModelForm. So have a look params description …

  • parent_model*: Provide the foreign key model. which was defined in model foreign key.
  • model*: Define the model class which was created a formset.
  • form*: Define the model form which model was used in model param
  • formset: Provide model formset whcih was override BaseInlineFormSet, by default BaseInlineFormSet is provided by django.
  • fk_name: If your model contains more than one foreign key then you must provide fk_name, by default it is none.
  • fields: Provide the model form fields to include as part of the model to create the model formset — just like the fields meta model form option
  • exclude: The defined model meta defines deleting model form fields as part of the model to create a model formset similar to the form option.
  • extra: Provide the number of the extra fields, you want to show, by default was 3.
  • can_order: Specify true if you want to control your formset. by default it is false.
  • can_delete:  By default formset items is deletable. providing False value, can’t delete formset item
  • max_num: Provide the number of max formset items you want to limit.
  • validate_max: Raise the number of max formset items validation. by default it’s False.
  • formfield_callback: Providing formfield_callback to customize the model form field. this method will execute prior to creating a form field from a model field.
  • widgets: Providing custom widgets to customization form fields, default it is none.
  • localized_fields: localized_fields makes models formset field as multiple languages support.. it like the model form meta option.
  • labels: Providing label overrides formset field labels. it like model form meta option
  • helper_texts: Providing helper_text overrides formset field helper tests. it like model form meta option
  • error_messages: Overriding error messages for formset form field. it like model form meta option
  • min_num: Provide the number of min formset items you want to limit.
  • validate_min: Raise the number of min formset items validation. by default it’s False.
  • field_classess: providing field_classess overriding field classes for the model form to create the model formset.

In Django 3.2 version two fields added

  • absolute_max:
  • can_delete_extra: control extra fields delectable, by default it’s False.

Example of  inlineformset_factory

For setup, create models

class Product(models.Model):
    """ Product """
    name = models.CharField(max_length=250)
    price = models.PositiveIntegerField()

    def __str__(self):
        return self.name

class ProductMeta(models.Model):
    """ Product Meta """
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    name = models.CharField('Property', max_length=50)
    value = models.CharField(max_length=200, blank=True, null=True)

    def __str__(self):
        return f'Id: {self.pk }, {self.name} for ProductId: {self.product.pk}'

Great we created models call Product and ProductMeta. So we are ready for formset example

Inline Formset factory

  1. Create inline form Factory

    In forms.py file create product model form and product meta model form

    from django import forms
    from django.forms import inlineformset_factory
    
    from .models import Product, ProductMeta
    
    
    class ProductForm(forms.ModelForm):
        class Meta:
            model = Product
            fields = ('name', 'price')
    
    
    class ProductMetaForm(forms.ModelForm):
        class Meta:
            model = ProductMeta
            fields = ('name', 'value')
    
    
    ProductMetaInlineFormset = inlineformset_factory(
        Product,
        ProductMeta,
        form=ProductMetaForm,
        extra=5,
        # max_num=5,
        # fk_name=None,
        # fields=None, exclude=None, 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
    )
    

     

  2. So now create a view for inline model form

    in view.py, create ProductCreate view

    from django.forms import modelformset_factory
    from django.shortcuts import render, get_object_or_404
    from django.views.generic import UpdateView, ListView, CreateView
    from django.shortcuts import redirect
    from django.urls import reverse
    
    from .models import Product
    from .forms import ProductForm, ProductMetaInlineFormset
    
    
    class ProductCreateView(CreateView):
        form_class = ProductForm
        template_name = 'product/product_form.html'
    
        def get_context_data(self, **kwargs):
            context = super(ProductCreateView, self).get_context_data(**kwargs)
    
            context['product_meta_formset'] = ProductMetaInlineFormset()
            return context
    
        def post(self, request, *args, **kwargs):
            self.object = None
            form_class = self.get_form_class()
            form = self.get_form(form_class)
            product_meta_formset = ProductMetaInlineFormset(self.request.POST)
            if form.is_valid() and product_meta_formset.is_valid():
                return self.form_valid(form, product_meta_formset)
            else:
                return self.form_invalid(form, product_meta_formset)
    
        def form_valid(self, form, product_meta_formset):
            self.object = form.save(commit=False)
            self.object.save()
            # saving ProductMeta Instances
            product_metas = product_meta_formset.save(commit=False)
            for meta in product_metas:
                meta.product = self.object
                meta.save()
            return redirect(reverse("product:product_list"))
    
        def form_invalid(self, form, product_meta_formset):
            return self.render_to_response(
                self.get_context_data(form=form,
                                      product_meta_formset=product_meta_formset
                                      )
            )
  3. Attach view to URL in Django

    in urls.py file

    from django.urls import path
    
    from .views import ProductListView, ProductCreateView
    
    app_name = 'product'
    urlpatterns = [
        # ...
        path('products', ProductListView.as_view(), name="product_list"),
        path('products/create', ProductCreateView.as_view(), name="product_form"),
        # ...
    ]
  4. Template view

    create product/product_form.html template

    <div class="container mt-4">
        <form method="post">
            {% csrf_token %}
            <div class="card">
                <div class="card-header">
                    <h4>Create Product</h4>
                </div>
                <div class="card-body">
                    {{ form.non_form_errors }}
                    {{ form.as_p }}
                    <h5 class="text-info">Add Product Metas</h5>
                    {{ product_meta_formset.non_form_errors }}
                    {{ product_meta_formset.management_form }}
                    {% for form in product_meta_formset %}
                    <div class="d-flex py-1 inline {{ product_meta_formset.prefix }}">
                        <div>{{form.name.label}}: {{ form.name }}</div>
                        <div class="ml-4">{{form.value.label}}: {{ form.value }}</div>
                        {% if product_meta_formset.can_delete %}
                            <div class="ml-4">{{ form.DELETE }} {{ form.DELETE.label }}</div>
                        {% endif %}
                    </div>
                    {% endfor %}
                </div>
            </div>
            <div class="mt-3 mb-5">
                <button type="submit" class="px-5 btn btn-info">Submit</button>
            </div>
        </form>
    </div>
  5. Checkout UI view

    formfactory

Related posts

Django Send Email with AWS Simple Email Service(SES)

Sajal Mia

How to create a virtual environment for Django?

Sajal Mia

How to create Django form?

Sajal Mia

1 comment

psedits 12/07/2021 at 7:37 PM

excellent post

Reply

Leave a Comment