Detailed explanation of django Form form processing

In the daily life of web development, user input processing is an unavoidable core link - from simple message boards, login and registration, to complex content editing pages, a form solution that can take into account efficiency, security, and ease of use is required. Django's official Form system perfectly meets this need: it can not only manually build flexible HTML forms, but also seamlessly connect with Model to generate CRUD pages, and can even process form data in batches.

Today’s article will take you from the basics to common advanced usage to avoid pitfalls and lightning protection~


📚 Let’s highlight the key points first: this article covers these contents

  • What are the core capabilities of django Form?
  • Build a complete basic form that can run from scratch
  • Commonly used fields, custom validation, and widget beautification
  • The most labor-saving rapid development of ModelForm
  • Basic details of forms security

1. Core capabilities of django Form

Many novices may think that "handwriting HTML forms + writing regular verification on the back end will suffice?" However, if you actually use Django Form, you will find that it saves you 90% of repeated work:

  1. Automatically generate HTML tags: No need to write duplicate onesinput/select
  2. Full-link data verification: From field format to business logic, the front-end prompts the back-end to reveal the details
  3. Automatic error processing and display: No need to assemble error messages yourself and pass them to the template
  4. Model one-click binding: Write a Form to complete addition, deletion, modification and query
  5. CSRF security automatic protection: As long as it is written according to the specifications, no additional processing of core tokens is required

2. Build a running basic Form from scratch

2.1 Define form class

Similar to defining Django Model, Form also inheritsforms.Form, define class attributes as form fields:

# app/forms.py
from django import forms

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100, label="你的昵称")
    email = forms.EmailField(label="常用邮箱")
    subject = forms.CharField(max_length=200, label="留言主题")
    message = forms.CharField(widget=forms.Textarea, label="详细内容")
    # required=False默认不勾选,也不会校验必填
    subscribe = forms.BooleanField(required=False, label="订阅我们的更新")

2.2 View layer processing logic

Views are the bridge connecting forms and data, and the core must be clearly distinguished.GET/POSTTwo kinds of requests:

# app/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import ContactForm

def contact_view(request):
    if request.method == "POST":
        # 用POST数据实例化表单(绑定数据)
        form = ContactForm(request.POST)
        # 先做全链路验证
        if form.is_valid():
            # 验证通过后,取cleaned_data(已转成Python对应类型的安全数据)
            name = form.cleaned_data["name"]
            email = form.cleaned_data["email"]
            # 处理数据:比如发邮件、存数据库
            # send_contact_mail(name, email, ...)
            messages.success(request, "留言发送成功!")
            return redirect("contact")  # 成功后重定向避免重复提交
    else:
        # GET请求返回空表单(未绑定数据)
        form = ContactForm()
    # 不管POST/GET失败,都把当前表单(空的或带错误的)传给模板
    return render(request, "contact.html", {"form": form})

2.3 Template layer rendering

Django provides three quick rendering methods, and also supports manual splitting of fields to customize styles:

<!-- templates/contact.html -->
<form method="post">
    {% csrf_token %}  <!-- 必加!否则会被django拦截 -->
    
    <!-- 快捷方式1:as_p 每个字段包在<p>里 -->
    {{ form.as_p }}
    
    <!-- 快捷方式2:as_ul 包在<li>里(记得加<ul>标签) -->
    <!-- <ul>{{ form.as_ul }}</ul> -->
    
    <!-- 快捷方式3:as_table 包在<tr><td>里(记得加<table>) -->
    <!-- <table>{{ form.as_table }}</table> -->
    
    <button type="submit" class="btn btn-primary">发送留言</button>
</form>

3. Commonly used advanced functions

3.1 Field types and Widgets

Common fields

The above example uses basic text, email, text fields, and many more practical ones:

# app/forms.py
from django import forms

class UserProfileForm(forms.Form):
    # 带长度限制和提示的用户名
    username = forms.CharField(
        max_length=150, min_length=3,
        label="用户名", help_text="3-150个字符,支持字母数字下划线"
    )
    # 密码输入框(隐藏内容)
    password = forms.CharField(widget=forms.PasswordInput, min_length=8)
    # 单选下拉框
    gender = forms.ChoiceField(
        choices=[("M", "男"), ("F", "女"), ("O", "其他")],
        label="性别"
    )
    # 多选复选框(默认是列表,用CheckboxSelectMultiple转成复选)
    hobbies = forms.MultipleChoiceField(
        choices=[("reading", "阅读"), ("sports", "运动"), ("music", "音乐")],
        widget=forms.CheckboxSelectMultiple, label="兴趣爱好"
    )
    # HTML5原生日期选择器
    birth_date = forms.DateField(
        widget=forms.DateInput(attrs={"type": "date"}), label="出生日期"
    )

Widget custom appearance

Widget is the "HTML coat" of the field, which can be passedattrsAdd class names, placeholders, etc.:

# app/forms.py
from django import forms

class SearchForm(forms.Form):
    query = forms.CharField(
        max_length=200,
        widget=forms.TextInput(attrs={
            "class": "form-control",  # 适配Bootstrap
            "placeholder": "搜索你感兴趣的内容...",
            "id": "global-search"
        }),
        label=""  # 隐藏标签
    )

3.2 Form validation

Django's validation is hierarchical, from field type → field's own rules → custom field validation → overall form validation:

Custom field validation

The format isdef clean_字段名(self):, and finally the cleaned field value must be returned:

# app/forms.py
from django import forms
from .models import Article  # 假设你有Article模型

class ArticleForm(forms.Form):
    title = forms.CharField(max_length=200, label="文章标题")
    content = forms.CharField(widget=forms.Textarea, label="正文")

    def clean_title(self):
        title = self.cleaned_data["title"]
        # 规则1:至少5个字符
        if len(title) < 5:
            raise forms.ValidationError("标题太短啦,至少5个字符")
        # 规则2:不能和已有的重复
        if Article.objects.filter(title=title).exists():
            raise forms.ValidationError("这个标题已经被用过了")
        return title  # 必须返回!

Overall form validation

The format isdef clean(self):, you can check across fields:

class ArticleForm(forms.Form):
    # ... 上面的字段 ...

    def clean(self):
        # 先调用父类的clean,获取所有清洗后的字段
        cleaned_data = super().clean()
        title = cleaned_data.get("title")
        content = cleaned_data.get("content")
        
        # 规则:标题不能完全出现在正文开头
        if title and content and content.startswith(title):
            raise forms.ValidationError("正文开头不能直接复制标题")
        
        return cleaned_data

4. The most labor-saving rapid development of ModelForm

If your form is for adding, deleting, or modifying data of a certain Model, then just use ModelForm. It will automatically inherit the Model's field type, length limit, and validation rules:

4.1 Basic definition

# app/forms.py
from django import forms
from .models import Article

class ArticleModelForm(forms.ModelForm):
    class Meta:  # 必须用这个内部类配置
        model = Article  # 绑定的Model
        fields = ["title", "content", "category", "status"]  # 要显示的字段(推荐显式写)
        # 或者 exclude = ["author", "created_at"]  # 排除某些字段
        
        # 覆盖Model默认的Widget
        widgets = {
            "content": forms.Textarea(attrs={"class": "form-control", "rows": 10}),
            "status": forms.Select(attrs={"class": "form-select"}),
        }
        
        # 覆盖默认的标签
        labels = {
            "category": "文章分类",
        }

4.2 Addition, deletion and modification of view layer

Added (automatically save Model)

# app/views.py
from django.contrib.auth.decorators import login_required

@login_required
def create_article(request):
    if request.method == "POST":
        form = ArticleModelForm(request.POST)
        if form.is_valid():
            # commit=False:先不存数据库,用来补充Model里没在表单显示的字段
            article = form.save(commit=False)
            article.author = request.user  # 补充当前登录用户
            article.save()  # 真正存数据库
            messages.success(request, "文章发布成功!")
            return redirect("article_detail", pk=article.pk)
    else:
        form = ArticleModelForm()
    return render(request, "create_article.html", {"form": form})

Edit (bind instance)

@login_required
def edit_article(request, pk):
    # 先获取要编辑的文章,找不到404
    article = get_object_or_404(Article, pk=pk)
    # 检查权限
    if article.author != request.user:
        messages.error(request, "您没有权限编辑这篇文章")
        return redirect("article_list")
    
    if request.method == "POST":
        # 绑定数据+实例,这样save()就是更新而不是新增
        form = ArticleModelForm(request.POST, instance=article)
        if form.is_valid():
            form.save()
            messages.success(request, "文章更新成功!")
            return redirect("article_detail", pk=article.pk)
    else:
        # GET请求绑定实例,自动填充已有内容
        form = ArticleModelForm(instance=article)
    return render(request, "edit_article.html", {"form": form, "article": article})

5. Basic details of form security

5.1 CSRF protection

Django enables CSRF middleware by default. All POST forms must add{% csrf_token %}Template tag, otherwise 403 Forbidden will be returned.

If it is an AJAX request, you can add a token in the following ways:

// 从Cookie中读取CSRF令牌
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

// 全局配置AJAX
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    }
});

5.2 Never trust user input

Even if Django does the verification for you, it is best to add another layer to the business logic: such as filtering spam keywords, limiting the number of links, etc.


6. Summary

The django Form system is the "Swiss Army Knife" of web development. It is simple to get started but powerful:

  • For daily simple formsforms.Form, flexible and controllable
  • Used for CRUD binding of ModelModelForm, save 90% of code
  • Verification rules are clearly layered to meet various business needs
  • Comes with CSRF protection, no need to manually handle core security

If you want to learn more, you can check out Form章节 of Django’s official documentation~