📝 Html Css

Django: Forms in Templates

P
Author
Pyland
📅
Published
30.06.2026
⏱️
Reading time
2 min
👁️
Views
82
🌳
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

📝

pytest-django: Testing Django

Охватываемые темы: Installation, @pytest.mark.djangodb, Fixtures, Testing views.

📅 30.06.2026 👁️ 138
📝

What is an ORM

ORM (Object-Relational Mapping) is a technology that lets you work with a database through Python...

📅 30.06.2026 👁️ 131
📝

SQLite in Python: Persistent Memory for Agents

SQLite is a relational database built into Python. It stores data in a single file...

📅 30.06.2026 👁️ 84

Did you like the article?

Subscribe to our updates and receive new articles first. Grow with PyLand!