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
-
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 )
-
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 ) )
-
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"), # ... ]
-
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>
-
Checkout UI view
10 comments
excellent post
Wonderful website. Lots of useful info here. I’m sending it to several friends ans also sharing in delicious. And certainly, thanks for your effort!
I like what you guys tend to be up too. Such clever work and coverage!
Keep up the wonderful works guys I’ve included you guys to my personal blogroll.
Hi there, I enjoy reading all of your article post.
I wanted to write a little comment to support you.
Appreciate this post. Let me try it out.
This is the perfect web site for anybody who wishes to find out about
this topic. You understand so much its almost tough to argue with you (not that I personally would want
to…HaHa). You certainly put a new spin on a subject which
has been discussed for years. Wonderful stuff, just excellent!
Awesome blog article.Really thank you! Fantastic.
Pretty part of content. I simply stumbled upon your web site and in accession capital
to claim that I get actually enjoyed account
your blog posts. Anyway I’ll be subscribing to your augment
or even I achievement you get entry to constantly fast.
There is noticeably a bundle to find out about this. I assume you made certain good points in features also.
Some really interesting information, well written and generally user pleasant.
Comments are closed.