Django inlineformset_factory

I was doing a Human Resource Management System. Before I started using " inlineformset_factory", which help us bring many models into one form page with one submit button, Department model used to come in select- option for adding staff in departments. But now it is coming as input field. How can I solve this issue? Thanks.

Welcome @dagi316 !

We’d need to see the models, forms and templates involved here to even begin to try and understand what might be happening.

Note: When posting code and templates here, enclose the code between lines of three backtick - ` characters. This means you’ll have a line of ```, then your code, then another line of ```. This forces the forum software to keep your code properly formatted.

1 Like

#models.py

class Departments(models.Model):
    department = models.CharField(max_length=20)

    def __str__(self):
        return self.department


class Staff(models.Model):
    class Sex(models.TextChoices):
        MALE = "M"
        FEMALE = "F"

    department = models.ForeignKey(
        Departments, on_delete=models.CASCADE, blank=True, null=True
    )
    position = models.CharField(max_length=20, blank=True, null=True)
    first_Name = models.CharField(max_length=20, blank=True, null=True)
    middle_Name = models.CharField(max_length=20, blank=True, null=True)
    last_Name = models.CharField(max_length=20, blank=True, null=True)
    sex = models.CharField(max_length=20, choices=Sex.choices, blank=True, null=True)
    dob = models.DateField(blank=True, null=True)
    date_Hired = models.DateField(blank=True, null=True)
    profile_Pic = models.ImageField(blank=True, null=True, default="user-default.png")
    id_Pic = models.ImageField(blank=True, null=True, default="default.jpg")

    def __str__(self):
        return f"{self.first_Name} {self.last_Name}"


class StaffAddress(models.Model):
    staff = models.ForeignKey(Staff, on_delete=models.CASCADE)
    country = models.CharField(max_length=20, default="Ethiopia")
    city = models.CharField(max_length=20, blank=True, null=True)
    subcity = models.CharField(max_length=20, blank=True, null=True)
    mobile_Phone = models.PositiveIntegerField(blank=True, null=False)
    House_Phone = models.PositiveIntegerField(blank=True, null=False)
    email = models.CharField(max_length=20, blank=True, null=True)
    p_o_Box = models.PositiveIntegerField(blank=True, null=True)
    postal_Code = models.PositiveIntegerField(default=0)

    def __str__(self):
        return self.country

#forms.py

from django import forms
from django.forms import ModelForm, widgets
from django.forms import inlineformset_factory
from .models import ( Departments, Staff,StaffAddress )


class DepartmentsForm(forms.ModelForm):
    class Meta:
        model = Departments
        fields = ["department"]

    def __init__(self, *args, **kwargs):
        super(DepartmentsForm, self).__init__(*args, **kwargs)


class StaffForm(forms.ModelForm):
    class Meta:
        model = Staff
        fields = "__all__"

        widgets = {
            "dob": forms.DateInput(
                attrs={
                    "class": "form-control date",
                    "type": "date",
                }
            ),
            "date_Hired": forms.DateInput(
                attrs={
                    "class": "form-control date",
                    "type": "date",
                }
            ),
        }

    def __init__(self, *args, **kwargs):
        super(StaffForm, self).__init__(*args, **kwargs)


class StaffAddressForm(forms.ModelForm):
    class Meta:
        model = StaffAddress
        fields = "__all__"

    def __init__(self, *args, **kwargs):
        super(StaffAddressForm, self).__init__(*args, **kwargs)

#  ///////// Inline Formset //////// /#

StaffFormSet = inlineformset_factory(
    Departments, Staff, form=StaffForm, extra=1, can_delete=True
)
StaffAddressFormSet = inlineformset_factory(
    Staff, StaffAddress, form=StaffAddressForm, extra=1, can_delete=True
)

#views.py

from django.shortcuts import render, redirect

# Create your views here.
from .models import (Departments, Staff, StaffAddress )
from .forms import ( DepartmentsForm, StaffForm, StaffFormSet,StaffAddressFormSet)


def department_List(request):
    departments = Departments.objects.all()
    number_Of_Staff = Staff.objects.count()

    context = {
        "departments": departments,
        "number_Of_Staff": number_Of_Staff,
    }

    return render(request, "departments/department_List.html", context)


def staff_List(request, pk):
    department = Departments.objects.get(id=pk)
    allstaffs = department.staff_set.all()

    number_Of_Staff = allstaffs.count()

    context = {
        "allstaffs": allstaffs,
        "department": department,
        "number_Of_Staff": number_Of_Staff,
    }

    return render(request, "departments/Staff_List.html", context)


def staff_Details(request, pk):
    department = Departments.objects.filter(id=pk).first()
    staff = Staff.objects.get(id=pk)

    # //////////////////////////////////////
    # StaffAddress Model
    staffaddress = StaffAddress.objects.all()
    individual_address = staff.staffaddress_set.all()
    context = {
        "department": department,
        "staff": staff,
        #
        "staffaddress": staffaddress,
        "individual_address": individual_address,
     
    }
    return render(request, "departments/staff_Details.html", context)


 

# CRUD
# Add Staff / Create


def add_Department(request):
    deptform = DepartmentsForm()
    #
    if request.method == "POST":
        deptform = DepartmentsForm(request.POST)
        if deptform.is_valid():
            deptform.save()
            return redirect("department_List")

    context = {
        "deptform": deptform,
    }
    return render(request, "departments/add_Departments.html", context)


def add_Staff(request):
    staffform = StaffForm()
    if request.method == "POST":
        staffform = StaffForm(request.POST, request.FILES, request.POST)
        if staffform.is_valid():
            staffform.save()
            return redirect("department_List")

    context = {
        "staffform": staffform,
    }
    return render(request, "departments/add_Staff_Form.html", context)


def update_Staff(request, pk):
    staff = Staff.objects.get(id=pk)
    staffform = StaffForm(instance=staff)
    #
    if request.method == "POST":
        staffform = StaffForm(request.POST, request.FILES, instance=staff)
        if staffform.is_valid():
            staffform.save()
            return redirect("department_List")

    context = {
        "staffform": staffform,
    }
    return render(request, "departments/add_Staff_Form.html", context)


def delete_Staff(request, pk):
    staff = Staff.objects.get(id=pk)
    if request.method == "POST":
        staff.delete()
        return redirect("department_List")

    context = {
        "object": staff,
    }
    return render(request, "departments/delete_Confirmation.html", context)
 

#urls.py

from django.urls import path
from . import views
from .views import  *


urlpatterns = [
    path("", views.department_List, name="department_List"),
    path("staff_List/<str:pk>/", views.staff_List, name="staff_List"),
    path("staff_Details/<str:pk>/", views.staff_Details, name="staff_Details"),
    # Create/ Add
    path("add_Department/", views.add_Department, name="add_Department"),
    path("add_Staff/", views.add_Staff, name="add_Staff"),
    path("update_Staff/<str:pk>/", views.update_Staff, name="update_Staff"),
    path("delete_Staff/<str:pk>/", views.delete_Staff, name="delete_Staff"),
]

#add_Staff_form.html

{% extends "main.html" %}

<!-- SPACE-->

{% block content %}

<!-- SPACE-->

<div class="container mx-5 p-5">
  <h4>Add Staff</h4>

  <form action="" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    <!-- SPACE-->

    {{ staffform.as_p }}

    <!-- SPACE-->

    <hr />

    <input class="btn btn-success" type="submit" value="Submit" />
  </form>
</div>

{% endblock content %}

You’ve posted multiple forms and views here - which one should I be focused on?

From your original post:

My interpretation of what you’ve written is that you have a view that is rendering a form. This form contains a field named department? This field is being rendered as a text input field instead of a select field, is that correct?

If so, which form and view are exhibiting this behavior?

Yes. Exactly. I just want it to select option , to add the staff in to particular dept.

Focus on views.py and inline formset.
May be let me share what I tried. Focus on the view,-> add_Staff view.

from django.shortcuts import get_object_or_404, render, redirect

# Create your views here.
from .models import (Departments,Staff, StaffAddress)
from .forms import (DepartmentsForm, StaffForm,StaffFormSet,StaffAddressFormSet)


def department_List(request):
    departments = Departments.objects.all()
    number_Of_Staff = Staff.objects.count()

    context = {
        "departments": departments,
        "number_Of_Staff": number_Of_Staff,
    }

    return render(request, "departments/department_List.html", context)


def staff_List(request, pk):
    department = Departments.objects.get(id=pk)
    allstaffs = department.staff_set.all()

    number_Of_Staff = allstaffs.count()

    context = {
        "allstaffs": allstaffs,
        "department": department,
        "number_Of_Staff": number_Of_Staff,
    }

    return render(request, "departments/Staff_List.html", context)


def staff_Details(request, pk):
    department = Departments.objects.filter(id=pk).first()
    staff = Staff.objects.get(id=pk)

    # //////////////////////////////////////
    # StaffAddress Model
    staffaddress = StaffAddress.objects.all()
    individual_address = staff.staffaddress_set.all()
 

    context = {
        "department": department,
        "staff": staff,
        #
        "staffaddress": staffaddress,
        "individual_address": individual_address,
         
    }
    return render(request, "departments/staff_Details.html", context)


# CRUD
# Add Staff / Create


def add_Department(request):
    deptform = DepartmentsForm()
    #
    if request.method == "POST":
        deptform = DepartmentsForm(request.POST)
        if deptform.is_valid():
            deptform.save()
            return redirect("department_List")

    context = {
        "deptform": deptform,
    }
    return render(request, "departments/add_Departments.html", context)


def add_Staff(request):
    if request.method == "POST":
        department_form = DepartmentsForm(request.POST)
        staff_formset = StaffFormSet(request.POST)
        if department_form.is_valid():
            department = department_form.save()
            staff_formset = StaffFormSet(request.POST, instance=department)
            if staff_formset.is_valid():
                staff_instances = staff_formset.save()
            for staff in staff_instances:
                staffaddress_formSet = StaffAddressFormSet(request.POST, instance=staff)
                if staffaddress_formSet.is_valid():
                    staffaddress_formSet.save()
            return redirect("department_List")
    else:
        department_form = DepartmentsForm()
        staff_formset = StaffFormSet(instance=Departments())
        staffaddress_formSet = StaffAddressFormSet(instance=Staff())
         

        context = {
            "department_form": department_form,
            "staff_formset": staff_formset,
            "staffaddress_formSet": staffaddress_formSet,
             
        }
    return render(request, "departments/add_Staff_Form.html", context)


def update_Staff(request, pk):
    staff = Staff.objects.get(id=pk)
    staffform = StaffForm(instance=staff)
    #
    if request.method == "POST":
        staffform = StaffForm(request.POST, request.FILES, instance=staff)
        if staffform.is_valid():
            staffform.save()
            return redirect("department_List")

    context = {
        "staffform": staffform,
    }
    return render(request, "departments/add_Staff_Form.html", context)


def delete_Staff(request, pk):
    staff = Staff.objects.get(id=pk)
    if request.method == "POST":
        staff.delete()
        return redirect("department_List")

    context = {
        "object": staff,
    }
    return render(request, "departments/delete_Confirmation.html", context)

 

If it’s the DepartmentsForm that you’re asking about, that makes sense. The department field is a character field. That’s going to be rendered as an input element.

Yes sir. Ho can I solve it please ?

Solve what? Everything is working as it should.

The select list gets generated for a foreign key reference to a model. The text input field is used for the field within that model.

Using inlineFormset factory, it comes as input field. Without inlineFormset factory, It comes as a select option drop down.

I want it as a select option drop down, using inlineforms set factory.

Ok, let’s limit this to one form, and one view. Please post the form and the view rendering that form. Identify what field you want rendered, and the widget you want to use rendering it. Please do not post multiple forms and views - it only makes things confusing.

okay…

#forms.py

class StaffForm(forms.ModelForm):
    class Meta:
        model = Staff
        fields = "__all__"

        widgets = {
            "dob": forms.DateInput(
                attrs={
                    "class": "form-control date",
                    "type": "date",
                }
            ),
            "date_Hired": forms.DateInput(
                attrs={
                    "class": "form-control date",
                    "type": "date",
                }
            ),
        }

    def __init__(self, *args, **kwargs):
        super(StaffForm, self).__init__(*args, **kwargs)

#views.py

def add_Staff(request):
    if request.method == "POST":
        department_form = DepartmentsForm(request.POST)
        staff_formset = StaffFormSet(request.POST)
        if department_form.is_valid():
            department = department_form.save()
            staff_formset = StaffFormSet(request.POST, instance=department)
            if staff_formset.is_valid():
                staff_instances = staff_formset.save()
            for staff in staff_instances:
                staffaddress_formSet = StaffAddressFormSet(request.POST, instance=staff)
                if staffaddress_formSet.is_valid():
                    staffaddress_formSet.save()
            return redirect("department_List")
    else:
        department_form = DepartmentsForm()
        staff_formset = StaffFormSet(instance=Departments())
        staffaddress_formSet = StaffAddressFormSet(instance=Staff())
         

        context = {
            "department_form": department_form,
            "staff_formset": staff_formset,
            "staffaddress_formSet": staffaddress_formSet,
             
        }
    return render(request, "departments/add_Staff_Form.html", context)

And what specifically is wrong with what gets rendered here, and what do you want it to be?

How can I make it like this using inlineformset factory?

What does it look like now?

What does this template look like?

It Looks like normal Input field.

I’m still not clear on how the image of the page you posted above was generated by the forms and templates we’re talking about here.

The select option works fine WITHOUT inlineformset_factory. This one is clear.

But the problem arise when I started using inlineformset_factory. It brings the Department field as input field. I want the Department field to be select - option field WITH inlineformset_factory. I posted the add_Staff.view alternative that I tried with inlineformset_factory. May be if you can check that.

Your template shows:
{{ staffform.as_p }}

Your view is shown as:

def add_Staff(request):
    if request.method == "POST":
        department_form = DepartmentsForm(request.POST)
        staff_formset = StaffFormSet(request.POST)
        if department_form.is_valid():
            department = department_form.save()
            staff_formset = StaffFormSet(request.POST, instance=department)
            if staff_formset.is_valid():
                staff_instances = staff_formset.save()
            for staff in staff_instances:
                staffaddress_formSet = StaffAddressFormSet(request.POST, instance=staff)
                if staffaddress_formSet.is_valid():
                    staffaddress_formSet.save()
            return redirect("department_List")
    else:
        department_form = DepartmentsForm()
        staff_formset = StaffFormSet(instance=Departments())
        staffaddress_formSet = StaffAddressFormSet(instance=Staff())
         

        context = {
            "department_form": department_form,
            "staff_formset": staff_formset,
            "staffaddress_formSet": staffaddress_formSet,
             
        }
    return render(request, "departments/add_Staff_Form.html", context)

I do not see where you’re creating anything in the context named staffform, and so I don’t see how you’re rendering any form at all from this template.

Yes sir. This {{ staffform.as_p }} is without inlineformset_factory. But with inlineformset_factory it looks like this …

<form action="" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    <!-- SPACE-->
    {{ department_form.as_p }}
    <!-- SPACE-->
    {{ staff_formset.management_form }}
    <!-- SPACE-->

        {% for form in staff_formset %}
        <!-- SPACE-->
        {{ form.as_p }}
        <!-- SPACE-->
        {% endfor %}
    <!-- SPACE-->
    
    {{ staffaddress_formSet.management_form }}
    <!-- SPACE-->

        {% for form in staffaddress_formSet %}
        <!-- SPACE-->
        {{ form.as_p }}
        <!-- SPACE-->
        {% endfor %}

    <!-- SPACE-->

    <hr />

    <input class="btn btn-success" type="submit" value="Submit" />
  </form>