How to place inline formsets form fields manually in Django templates and I want to show only specific fields in Template forms

We need to place form fields in specific x,y location and field size as per the layout provided in the attached image manually in a bootstrap template page, below are our models.py & forms.py code

models.py:

from django.db import models
from django.utils import timezone
from datetime import datetime

def generateNewEmpID():
    today = datetime.now()
    YearMonthDay = today.strftime("%Y%m%d")
    newEmpIdPrefix = 'NT' + YearMonthDay

    try:
        last_employee_id = Employee.objects.all().last().employee_id  # Eg : NT2018012710001
    except AttributeError:
        last_employee_id = None

    if not last_employee_id:
        newEmpID = str(newEmpIdPrefix) + '10001'
        return newEmpID
    else:
        if last_employee_id[:10] == newEmpIdPrefix:

            oldEmpID_int = last_employee_id[11:]
            newEmpIDSuffix = int(oldEmpID_int) + 1

            newEmpID = newEmpIdPrefix + str(newEmpIDSuffix).zfill(5)
            return newEmpID
        else:
            newEmpID = str(newEmpIdPrefix) + '10001'
            return newEmpID


class Employee(models.Model):
    employee_id = models.IntegerField(max_length=30, default=generateNewEmpID, primary_key=True)
    first_name = models.CharField(max_length=30, blank=False, null=False)
    last_name = models.CharField(max_length=30, blank=False, null=False)
    account = models.CharField(max_length=100, blank=False, null=False)
    designation = models.CharField(max_length=30, blank=False, null=False)
    mail_id = models.CharField(max_length=100, blank=False, null=False)
    photo = models.ImageField(blank=True, upload_to='employee/photos')
    mobile_no = models.IntegerField(max_length=10)
    phone_no = models.CharField(max_length=100, blank=True, null=True)
    address = models.CharField(max_length=100)
    date_of_join = models.DateField(default=datetime.date())
    ctc_pa = models.IntegerField(max_length=10)
    pan_no = models.CharField(max_length=10)
    epfno = models.CharField(max_length=20)
    bank = models.CharField(max_length=100)
    bank_account = models.CharField(max_length=30)
    bank_ifsc = models.CharField(max_length=15)
    emergency_contact = models.CharField(max_length=100)
    created_date = models.DateTimeField(default=timezone.now)

        class Meta:
        managed = False
        db_table = 'employee'

class dependents(models.Model):
    employee_id = models.ForeignKey(Employee, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    relationship = models.CharField(max_length=100)
    age = models.IntegerField(max_length=2)

    class Meta:
        managed = False
        db_table = 'emp_dependents'

class education(models.Model):
    employee_id = models.ForeignKey(Employee, on_delete=models.CASCADE)
    course = models.CharField(max_length=100)
    branch = models.CharField(max_length=100)
    college = models.CharField(max_length=100)
    location = models.CharField(max_length=100)
    completion_date = models.DateField()
    percentage = models.IntegerField(max_length=3)

    class Meta:
        managed = False
        db_table = 'emp_education'

...................
...................
...................

forms.py

from django import forms
from .models import Employee, Education, Dependents
from django.forms import inlineformset_factory, ModelForm

class EmployeeForm(ModelForm):
    class Meta:
        model = Employee

class DependentsForm(ModelForm):
    class Meta:
        model = Dependents

class EducationForm(ModelForm):
    class Meta:
        model = Education

EmployeeDependentsFormSet = inlineformset_factory(Employee, Dependents, form=EducationForm, extra=1, can_delete=True, can_delete_extra=True)

EmployeeEducationFormSet = inlineformset_factory(Employee, Education, form=EducationForm, extra=1, can_delete=True, can_delete_extra=True)

We need to show only few columns on the template forms in newemp.html (bootstrap) as show in the below image.

enter image description here

To achieve above form layout we are supposed to place fields manually only in a bootstrap template.

How can we create a similar data form in django template using bootstrap.

tried with basic form with single model, but not able to do with parent/child(s) relationship

Fields that are not in the form need to be optional

You have to make sure the fields that you're not displaying in the form are optional in the model. If you don't do this, validation always fails. Please set blank=True null=True on them, and then run makemigrations followed by migrations. This includes the following fields on the Employee model: "mail_id", "mobile_no", "ctc_pa", "pan_no", "epfno", "bank", "bank_account", and "bank_ifsc".

Side notes on the model code

  • Use camel case and singular for model names. This is why I renamed the models education and dependents. See this.
  • Don't use max_length kwarg with models.IntegerField. This is simply ignored. Found this in the following fields: Employee.ctc_pa, Employee.mobile_no, Dependent.age, Education.percentage.
  • If you want the date fields to be set to now, you need to use auto_now_add (set when creating the instance), not the default kwarg. See this antipattern for more details.

The form code

I added the formsets as properties of the EmployeeForm so we can encapsulate initialization, validation, and persisting to the database in one class. Like this, the view code is very simple as we can treat the EmployeeForm like any other form.

# forms.py

class EmployeeForm(forms.ModelForm):

    class Meta:
        model = Employee
        fields = ['first_name', 'last_name', 'account', 'designation',
                  'address', 'emergency_contact', 'photo']
        widgets = {
            'first_name': TextInput(attrs={'class': 'form-control'}),
            'last_name': TextInput(attrs={'class': 'form-control'}),
            'account': TextInput(attrs={'class': 'form-control'}),
            'designation': TextInput(attrs={'class': 'form-control'}),
            'address': TextInput(attrs={'class': 'form-control'}),
            'emergency_contact': TextInput(attrs={'class': 'form-control'}),
            'photo': Textarea(attrs={'class': 'form-control'}),
        }

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

        # Initialize the formsets
        self.dependent_formset = DependentFormSet(
            data=kwargs.get('data'), files=kwargs.get('files'),
            instance=kwargs.get('instance'),
        )
        self.education_formset = EducationFormSet(
            data=kwargs.get('data'), files=kwargs.get('files'),
            instance=kwargs.get('instance'),
        )

    def is_valid(self):
        # Check the validity of formsets when you validate this form
        is_valid = super().is_valid()
        is_valid &= self.dependent_formset.is_valid()
        is_valid &= self.education_formset.is_valid()

        return is_valid

    def save(self, **kwargs):
        # After saving the base instance, also save the formsets
        saved_employee_instance = super().save(**kwargs)

        # Update the instance before saving. The initial instance have no PK.
        self.dependent_formset.instance = saved_employee_instance
        self.dependent_formset.save()
        self.education_formset.instance = saved_employee_instance
        self.education_formset.save()


class DependentForm(forms.ModelForm):

    class Meta:
        model = Dependent
        fields = '__all__'
        widgets = {
            'name': TextInput(attrs={'class': 'form-control'}),
            'relationship': TextInput(attrs={'class': 'form-control'}),
            'age': NumberInput(attrs={'class': 'form-control'}),
            'contact_number': TextInput(attrs={'class': 'form-control'}),
        }


class EducationForm(forms.ModelForm):

    class Meta:
        model = Education
        fields = '__all__'
        widgets = {
            'course': TextInput(attrs={'class': 'form-control'}),
            'branch': TextInput(attrs={'class': 'form-control'}),
            'college': TextInput(attrs={'class': 'form-control'}),
            'location': TextInput(attrs={'class': 'form-control'}),
            'completion_date': DateInput(attrs={'class': 'form-control',
                                                'type': 'date'}),
            'percentage': NumberInput(attrs={'class': 'form-control'}),
        }


DependentFormSet = inlineformset_factory(
    Employee, Dependent, form=DependentForm
)

EducationFormSet = inlineformset_factory(
    Employee, Education, form=EducationForm
)

View code

Assuming you need the code for create purposes. For update purposes, it's just a matter of creating another subclass of UpdateView.

# views.py

class EmployeeCreateView(CreateView):
    model = Employee
    form_class = EmployeeForm
    template_name = 'employee_form.html'

    def get_success_url(self):
        return reverse('success')

HTML Template code

I used Bootstrap grid to create the layout you proposed in the question. For the formsets, I used a table. Although this gives you a lot of room for customization, consider using django-crispy-forms package. It can reduce the amount of code in the template to a couple of lines which makes if more maintainable and less prone to errors.

{# employee_form.html #}

<head>...</head>
<body>

    <form method="post">
        {% csrf_token %}

        <div class="container my-5">
            {{ form.non_field_errors.as_p }}

            <div class="row">
                <div class="col-8">
                    <div class="row">
                        <div class="col">
                            <div class="row">
                                <div class="col-3">
                                    {{ form.first_name.label_tag }}
                                </div>

                                <div class="col">
                                    {{ form.first_name }} {{ form.first_name.errors }}
                                </div>
                            </div>
                        </div>

                        <div class="col">
                            <div class="row">
                                <div class="col-3">
                                    {{ form.last_name.label_tag }}
                                </div>

                                <div class="col">
                                    {{ form.last_name }} {{ form.last_name.errors }}
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col">
                            <div class="row">
                                <div class="col-3">
                                    {{ form.account.label_tag }}
                                </div>

                                <div class="col">
                                    {{ form.account }} {{ form.account.errors }}
                                </div>
                            </div>
                        </div>

                        <div class="col">
                            <div class="row">
                                <div class="col-3">
                                    {{ form.designation.label_tag }}
                                </div>
                                <div class="col">
                                    {{ form.designation }} {{ form.designation.errors }}
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-2">
                            {{ form.address.label_tag }}
                        </div>
                        <div class="col">
                            {{ form.address }} {{ form.address.errors }}
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-2">
                            {{ form.emergency_contact.label_tag }}
                        </div>

                        <div class="col">
                            {{ form.emergency_contact }} {{ form.emergency_contact.errors }}
                        </div>
                    </div>
                </div>

                <div class="col">
                    {{ form.photo }} {{ form.photo.errors }}
                </div>
            </div>

            <div class="row">
                <h2>Dependents</h2>

                {{ form.dependent_formset.non_field_errors }}

                <table class="table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Relationship</th>
                            <th>Age</th>
                            <th>Contact No.</th>
                            <th></th>  <!-- For edit button -->
                            <th></th>  <!-- For delete button -->
                        </tr>
                    </thead>

                    {{ form.dependent_formset.management_form }}

                    <tbody id="dependent-formset">
                        {% for dependent_form in form.dependent_formset %}
                            <tr>
                                <td>{{ dependent_form.name }} {{ dependent_form.name.errors }}</td>
                                <td>{{ dependent_form.relationship }} {{ dependent_form.relationship.errors }}</td>
                                <td>{{ dependent_form.age }} {{ dependent_form.age.errors }}</td>
                                <td>{{ dependent_form.contact_number }} {{ dependent_form.contact_number.errors }}</td>
                                <td>
                                    <!-- TODO: replace "href" attribute with a real URL to the edit view -->
                                    <a class="btn btn-sm btn-secondary" href="#">Edit</a>
                                </td>
                                <td>
                                    <!-- TODO: replace "href" attribute with a real URL to the delete view -->
                                    <a class="btn btn-sm btn-danger" href="#">Delete</a>
                                </td>
                            </tr>
                        {% endfor %}
                    </tbody>
                </table>

                <button class="btn btn-outline-secondary add-dependent-button" type="button">Add dependent</button>

                {% with dependent_empty_form=form.dependent_formset.empty_form %}
                    <table class="d-none">
                        <tbody id="dependent-empty-form">
                            <tr>
                                <td>{{ dependent_empty_form.name }} {{ dependent_empty_form.name.errors }}</td>
                                <td>{{ dependent_empty_form.relationship }} {{ dependent_empty_form.relationship.errors }}</td>
                                <td>{{ dependent_empty_form.age }} {{ dependent_empty_form.age.errors }}</td>
                                <td>{{ dependent_empty_form.contact_number }} {{ dependent_empty_form.contact_number.errors }}</td>
                                <td>
                                    <!-- TODO: replace "href" attribute with a real URL to the edit view -->
                                    <a class="btn btn-sm btn-secondary" href="#">Edit</a>
                                </td>
                                <td>
                                    <!-- TODO: replace "href" attribute with a real URL to the delete view -->
                                    <a class="btn btn-sm btn-danger" href="#">Delete</a>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                {% endwith %}
            </div>

            <div class="row">
                <h2>Education</h2>

                {{ form.education_formset.non_field_errors }}

                <table class="table">
                    <thead>
                        <tr>
                            <th>Course</th>
                            <th>Branch</th>
                            <th>College</th>
                            <th>Location</th>
                            <th>Completion Date</th>
                            <th>Grade / %</th>
                            <th></th>  <!-- For edit button -->
                            <th></th>  <!-- For delete button -->
                        </tr>
                    </thead>

                    {{ form.education_formset.management_form }}

                    <tbody id="education-formset">
                        {% for education_form in form.education_formset %}
                            <tr>
                                <td>{{ education_form.course }} {{ education_form.course.errors }}</td>
                                <td>{{ education_form.branch }} {{ education_form.branch.errors }}</td>
                                <td>{{ education_form.college }} {{ education_form.college.errors }}</td>
                                <td>{{ education_form.location }} {{ education_form.location.errors }}</td>
                                <td>{{ education_form.completion_date }} {{ education_form.completion_date.errors }}</td>
                                <td>{{ education_form.percentage }} {{ education_form.percentage.errors }}</td>
                                <td>
                                    <!-- TODO: replace "href" attribute with a real URL to the edit view -->
                                    <a class="btn btn-sm btn-secondary" href="#">Edit</a>
                                </td>
                                <td>
                                    <!-- TODO: replace "href" attribute with a real URL to the delete view -->
                                    <a class="btn btn-sm btn-danger" href="#">Delete</a>
                                </td>
                            </tr>
                        {% endfor %}
                    </tbody>
                </table>

                <button class="btn btn-outline-secondary add-education-button" type="button">Add education</button>

                {% with education_empty_form=form.education_formset.empty_form %}
                    <table class="d-none">
                        <tbody id="education-empty-form">
                            <tr>
                                <td>{{ education_empty_form.course }} {{ education_empty_form.course.errors }}</td>
                                <td>{{ education_empty_form.branch }} {{ education_empty_form.branch.errors }}</td>
                                <td>{{ education_empty_form.college }} {{ education_empty_form.college.errors }}</td>
                                <td>{{ education_empty_form.location }} {{ education_empty_form.location.errors }}</td>
                                <td>{{ education_empty_form.completion_date }} {{ education_empty_form.completion_date.errors }}</td>
                                <td>{{ education_empty_form.percentage }} {{ education_empty_form.percentage.errors }}</td>
                                <td>
                                    <!-- TODO: replace "href" attribute with a real URL to the edit view -->
                                    <a class="btn btn-sm btn-secondary" href="#">Edit</a>
                                </td>
                                <td>
                                    <!-- TODO: replace "href" attribute with a real URL to the delete view -->
                                    <a class="btn btn-sm btn-danger" href="#">Delete</a>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                {% endwith %}
            </div>

            <div class="row mt-5">
                <div class="col">
                    <label for="submit"><button class="btn btn-primary">Save</button></label>
                    <input id="submit" type="submit" value="Save" class="d-none" />
                </div>
            </div>
        </div>
    </form>

</body>

<footer>
    <!-- We need JQuery for the code below -->
    <script src="https://code.jquery.com/jquery-3.6.3.slim.min.js"
            integrity="sha256-ZwqZIVdD3iXNyGHbSYdsmWP//UBokj2FHAxKuSBKDSo="
            crossorigin="anonymous"></script>
    <!-- Script for dynamically adding forms to the formsets -->
    <script>
        /**
         * Adds an empty form to the formset.
         * NOTE: id args should be used without the "#" symbol.
         */
        function copyForm(formsetPrefix, formsetId, emptyFormId) {
            const total_forms = $(`#id_${formsetPrefix}-TOTAL_FORMS`);
            const total_forms_value = total_forms.val();

            // Use the value of total forms for the ID of the new form
            let empty_form_html = $(`#${emptyFormId}`).html();
            empty_form_html = empty_form_html.replace(/__prefix__/g, total_forms_value);
            $(`#${formsetId}`).append(empty_form_html);

            // Increment the value in the management form
            total_forms.val(parseInt(total_forms_value) + 1);
        }

        $('.add-dependent-button').click(function(e) {
            copyForm('dependent_set', 'dependent-formset', 'dependent-empty-form');
        });

        $('.add-education-button').click(function(e) {
            copyForm('education_set', 'education-formset', 'education-empty-form');
        })
    </script>
</footer>
</html>
Back to Top