๐Ÿ“ Html Css

Django: Forms in Templates

P
Author
PyLand Team
๐Ÿ“…
Published
30.06.2026
โฑ๏ธ
Reading time
2 min
๐Ÿ‘๏ธ
Views
294
๐ŸŒณ
Level
Advanced

A Django form is a Python class. In a template it becomes HTML. Let’s look at the rendering approaches: from automatic to fully manual.

Passing a Form to a Template

# forms.py
from django import forms

class CommentForm(forms.Form):
    name = forms.CharField(max_length=100, label="Name")
    email = forms.EmailField(label="Email")
    text = forms.CharField(widget=forms.Textarea, label="Comment")
# views.py
from django.shortcuts import render, redirect
from .forms import CommentForm

def add_comment(request, post_pk):
    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            # save data
            return redirect('post-detail', pk=post_pk)
    else:
        form = CommentForm()

    return render(request, 'blog/comment_form.html', {'form': form})

Automatic Rendering

The fastest approach โ€” let Django render the fields itself:

<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit" class="btn btn-primary">Submit</button>
</form>
Method HTML wrapper
{{ form.as_p }} Each field in a <p>
{{ form.as_div }} Each field in a <div> (Bootstrap-friendly)
{{ form.as_table }} Table with <tr>/<td>
{{ form.as_ul }} List with <li>

Limitation: automatic rendering doesn’t add Bootstrap classes โ€” you’ll need to style via CSS or widgets.

Manual Rendering: Full Control

Manual rendering gives you complete control over each field’s HTML:

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

  <div class="mb-3">
    {{ form.name.label_tag }}
    {{ form.name }}
    {% if form.name.errors %}
      <div class="invalid-feedback d-block">
        {{ form.name.errors }}
      </div>
    {% endif %}
  </div>

  <div class="mb-3">
    {{ form.email.label_tag }}
    {{ form.email }}
    {% if form.email.errors %}
      <div class="invalid-feedback d-block">
        {{ form.email.errors }}
      </div>
    {% endif %}
  </div>

  <div class="mb-3">
    {{ form.text.label_tag }}
    {{ form.text }}
    {% if form.text.errors %}
      <div class="invalid-feedback d-block">
        {{ form.text.errors }}
      </div>
    {% endif %}
  </div>

  <button type="submit" class="btn btn-primary">Submit</button>
</form>

{{ form.name }} renders the <input>, {{ form.name.label_tag }} renders the <label>, {{ form.name.errors }} renders validation errors.

Bootstrap Classes via Widgets

To avoid adding styles in the template, set classes on the form itself:

# forms.py
class CommentForm(forms.Form):
    name = forms.CharField(
        max_length=100,
        label="Name",
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': 'Your name',
        })
    )
    email = forms.EmailField(
        label="Email",
        widget=forms.EmailInput(attrs={'class': 'form-control'})
    )
    text = forms.CharField(
        label="Comment",
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 4,
        })
    )

Now {{ form.as_p }} already outputs fields with the form-control class.

Looping Over Fields

For forms with many fields, iterate over them:

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

  {% for field in form %}
    <div class="mb-3">
      {{ field.label_tag }}
      {{ field }}
      {% if field.help_text %}
        <small class="form-text text-muted">{{ field.help_text }}</small>
      {% endif %}
      {% for error in field.errors %}
        <div class="invalid-feedback d-block">{{ error }}</div>
      {% endfor %}
    </div>
  {% endfor %}

  {{ form.non_field_errors }}

  <button type="submit" class="btn btn-primary">Submit</button>
</form>

form.non_field_errors โ€” errors not tied to a specific field (e.g. from form.clean()).

Example: DevBlog Login Form

{% extends 'base.html' %}

{% block title %}Log In โ€” DevBlog{% endblock %}

{% block content %}
<div class="row justify-content-center">
  <div class="col-md-5">
    <h1 class="mb-4">Log In</h1>

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

      <div class="mb-3">
        <label for="{{ form.username.id_for_label }}" class="form-label">
          Username
        </label>
        {{ form.username }}
        {{ form.username.errors }}
      </div>

      <div class="mb-3">
        <label for="{{ form.password.id_for_label }}" class="form-label">
          Password
        </label>
        {{ form.password }}
        {{ form.password.errors }}
      </div>

      {% if form.non_field_errors %}
        <div class="alert alert-danger">{{ form.non_field_errors }}</div>
      {% endif %}

      <button type="submit" class="btn btn-primary w-100">Log In</button>
    </form>
  </div>
</div>
{% endblock %}

Your reaction to the article

๐Ÿ’ฌ Comments (0)

๐Ÿ” Sign in to leave a comment
๐Ÿšช Login
๐Ÿ’ญ

No comments yet

Be the first to share your opinion about this article!

๐Ÿ”— Similar

Similar articles

Continue learning with these materials

๐Ÿ“

Django: Template Tags

Template tags are logic inside HTML. Unlike {{ variable }} which only outputs a value,...

๐Ÿ“… 30.06.2026 ๐Ÿ‘๏ธ 306
๐Ÿ“

Django: Static Files

Static files are CSS, JavaScript, images, and fonts. Django handles them in a specific way:...

๐Ÿ“… 30.06.2026 ๐Ÿ‘๏ธ 290
๐Ÿ“

Django: Template Inheritance

Without inheritance, every HTML template would contain duplicated code: navigation, CSS/JS links, footer. Inheritance lets...

๐Ÿ“… 30.06.2026 ๐Ÿ‘๏ธ 319
๐ŸŽ“ Continue learning

Courses that cover this material

Visit the course to apply this material in practice.

Django RequestLab: from HTTP to secure production Open course curriculum Markup for Backend Developers Open course curriculum