处理表单是一件很复杂的事情。考虑一下Django 的Admin 站点,不同类型的大量数据项需要在一个表单中准备好、渲染成HTML、使用一个方便的界面编辑、返回给服务器、验证并清除,然后保存或者向后继续处理。
Django 的表单功能可以简化并自动化大部分这些工作,而且还可以比大部分程序员自己所编写的代码更安全。
Django 会处理表单工作中的三个显著不同的部分:
准备数据、重构数据,以便下一步提交。
为数据创建HTML 表单
接收并处理客户端提交的表单和数据
可以手工编写代码来实现,但是Django 可以帮你完成所有这些工作。
我们已经计划好了我们的 HTML 表单应该呈现的样子。在Django 中,我们的起始点是这里:
forms.py
from django import forms
class NameForm(forms.Form):your_name = forms.CharField(label='Your name', max_length=100)
它定义一个Form 类,只带有一个字段(your_name)。我们已经对这个字段使用一个友好的标签,当渲染时它将出现在
字段允许的最大长度通过max_length 定义。它完成两件事情。首先,它在HTML 的 上放置一个maxlength=”100” (这样浏览器将在第一时间阻止用户输入多于这个数目的字符)。它还意味着当Django 收到浏览器发送过来的表单时,它将验证数据的长度。
Form 的实例具有一个is_valid() 方法,它为所有的字段运行验证的程序。当调用这个方法时,如果所有的字段都包含合法的数据,它将:
返回True
将表单的数据放到cleaned_data 属性中。
完整的表单,第一次渲染时,看上去将像:
<label for&#61;"your_name">Your name: label>
"your_name" type&#61;"text" name&#61;"your_name" maxlength&#61;"100">
注意它不包含 标签和提交按钮。我们必须自己在模板中提供它们。
发送给Django 网站的表单数据通过一个视图处理&#xff0c;一般和发布这个表单的是同一个视图。这允许我们重用一些相同的逻辑。
要操作一个通过URL发布的表单&#xff0c;我们要在视图中实例表单。
views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from .forms import NameFormdef get_name(request):# if this is a POST request we need to process the form dataif request.method &#61;&#61; &#39;POST&#39;:# create a form instance and populate it with data from the request:form &#61; NameForm(request.POST)# check whether it&#39;s valid:if form.is_valid():# process the data in form.cleaned_data as required# ...# redirect to a new URL:return HttpResponseRedirect(&#39;/thanks/&#39;)# if a GET (or any other method) we&#39;ll create a blank formelse:form &#61; NameForm()return render(request, &#39;name.html&#39;, {&#39;form&#39;: form})
如果访问视图的是一个GET 请求&#xff0c;它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中。这是我们在第一次访问该URL 时预期发生的情况。
如果表单的提交使用POST 请求&#xff0c;那么视图将再次创建一个表单实例并使用请求中的数据填充它&#xff1a;form &#61; NameForm(request.POST)。这叫做”绑定数据至表单“&#xff08;它现在是一个绑定的表单&#xff09;。
我们调用表单的is_valid() 方法&#xff1b;如果它不为True&#xff0c;我们将带着这个表单返回到模板。这时表单不再为空&#xff08;未绑定&#xff09;&#xff0c;所以HTML 表单将用之前提交的数据填充&#xff0c;然后可以根据要求编辑并改正它。
如果is_valid() 为True&#xff0c;我们将能够在cleaned_data 属性中找到所有合法的表单数据。在发送HTTP 重定向给浏览器告诉它下一步的去向之前&#xff0c;我们可以用这个数据来更新数据库或者做其它处理。
我们不需要在name.html 模板中做很多工作。最简单的例子是&#xff1a;
<form action&#61;"/your-name/" method&#61;"post">{% csrf_token %}{{ form }}<input type&#61;"submit" value&#61;"Submit" />
form>
根据{{ form }}&#xff0c;所有的表单字段和它们的属性将通过Django 的模板语言拆分成HTML 标记 。
表单和跨站请求伪造的防护
Django 原生支持一个简单易用的跨站请求伪造的防护。当提交一个启用CSRF 防护的POST 表单时&#xff0c;你必须使用上面例子中的csrf_token 模板标签。然而&#xff0c;因为CSRF 防护在模板中不是与表单直接捆绑在一起的&#xff0c;这个标签在这篇文档的以下示例中将省略。
HTML5 输入类型和浏览器验证
如果你的表单包含URLField、EmailField 或其它整数字段类型&#xff0c;Django 将使用url、email和 number 这样的HTML5 输入类型。默认情况下&#xff0c;浏览器可能会对这些字段进行它们自身的验证&#xff0c;这些验证可能比Django 的验证更严格。如果你想禁用这个行为&#xff0c;请设置form 标签的novalidate 属性&#xff0c;或者指定一个不同的字段&#xff0c;如TextInput。
现在我们有了一个可以工作的网页表单&#xff0c;它通过Django Form 描述、通过视图处理并渲染成一个HTML 。
这是你入门所需要知道的所有内容&#xff0c;但是表单框架为了便利提供了更多的内容。一旦你理解了上面描述的基本处理过程&#xff0c;你应该可以理解表单系统的其它功能并准备好学习更多的底层机制。
required&#xff1a;是否可以为空。required&#61;True 不可以为空&#xff0c;required&#61;False 可以为空
max_length&#61;4 最多4个值&#xff0c;超过不会显示
min_length&#61;2 至少两个值&#xff0c;少于两个会返回提示信息
error_messages&#61;{‘required’: ‘邮箱不能为空’, ‘invalid’: ‘邮箱格式错误’} 自定义错误信息&#xff0c;invalid 是格式错误
widget&#61;forms.TextInput(attrs&#61;{‘class’: ‘c1’}) 给自动生成的input标签自定义class属性
widget&#61;forms.Textarea() 生成Textarea标签。widget默认生成input标签
models.py
from django.db import models
class Author(models.Model):"""作者"""name &#61; models.CharField(max_length&#61;100)age &#61; models.IntegerField()class BookType(models.Model):"""图书类型"""caption &#61; models.CharField(max_length&#61;64)class Book(models.Model):"""图书"""name &#61; models.CharField(max_length&#61;64)pages &#61; models.IntegerField()price &#61; models.DecimalField(max_digits&#61;10,decimal_places&#61;2)pubdate &#61; models.DateField()authors &#61; models.ManyToManyField(Author)book_type &#61; models.ForeignKey(BookType)
forms.py&#xff1a;
from django import forms
from app01 import modelsclass Form1(forms.Form):user &#61; forms.CharField(label&#61;&#39;Your name&#39;,widget&#61;forms.TextInput(attrs&#61;{&#39;class&#39;: &#39;c1&#39;}),error_messages&#61;{&#39;required&#39;: &#39;用户名不能为空&#39;}, )pwd &#61; forms.CharField(max_length&#61;4, min_length&#61;2, required&#61;True,error_messages &#61; {&#39;required&#39;: &#39;密码不能为空&#39;})email &#61; forms.EmailField(error_messages&#61;{&#39;required&#39;: &#39;邮箱不能为空&#39;, &#39;invalid&#39;: &#39;邮箱格式错误&#39;})memo &#61; forms.CharField(widget&#61;forms.Textarea(),error_messages&#61;{&#39;required&#39;: &#39;memo不能为空&#39;})vip_type &#61; ((0, u&#39;普通用户&#39;),(1, u&#39;高级用户&#39;),)vip &#61; forms.CharField(widget&#61;forms.widgets.Select(choices&#61;vip_type, attrs&#61;{&#39;class&#39;: &#39;form-control&#39;}))#写上以下代码就不用担心数据库添加了数据而不能及时获取了def __init__(self, *args, **kwargs):#每次创建Form1对象时执行init方法super(Form1, self).__init__(*args, **kwargs)self.fields[&#39;book_type&#39;] &#61; forms.CharField(widget&#61;forms.widgets.Select(choices&#61;models.BookType.objects.values_list(&#39;id&#39;, &#39;caption&#39;),attrs&#61;{&#39;class&#39;: "form-control"}))
HTML&#xff1a;
<html lang&#61;"en">
<head><meta charset&#61;"UTF-8"><title>Titletitle><style>.input-group{position: relative;padding: 20px;width: 250px;}.input-group input{width: 200px;display: inline-block;}.inline-group span{display: inline-block;position: absolute;height: 12px;font-size: 8px;border: 1px solid red;background-color: coral;color: white;top: 41px;left: 20px;width: 202px;}style>
head>
<body><form action&#61;"/form1/" method&#61;"POST">{% csrf_token %}
<div class&#61;"input-group">
<label>用户名:label>{{ form.user }}<span>{{ form.user.errors }}span>
div><div class&#61;"input-group">
<label>密码:label>{{ form.pwd }}<span>{{ form.pwd.errors }}span>
div><div class&#61;"input-group">
<label>邮箱:label>{{ form.email }}<span>{{ form.email.errors }}span>
div><div class&#61;"input-group">
<label>memo:label>{{ form.memo }}<span>{{ form.memo.errors }}span>
div>
<div class&#61;"input-group">
<label>会员等级:label>{{ form.vip }}<span>{{ form.vip.errors }}span>
div>
<div><input type&#61;"submit" value&#61;"提交" />
div>form>body>
html>
views.py
from django.shortcuts import render,HttpResponse
from app01.forms import Form1
from app01.models import *def form1(request):if request.method &#61;&#61; "POST":f &#61; Form1(request.POST)if f.is_valid():print(f.cleaned_data)else:return render(request,"account/form1.html",{"error":f.errors,"form":f})else:# 如果不是post提交数据&#xff0c;就不传参数创建对象&#xff0c;并将对象返回给前台&#xff0c;直接生成input标签&#xff0c;内容为空f &#61; Form1()return render(request,"account/form1.html",{"form":f})return render(request,"account/form1.html")
Django里面没有手机验证&#xff0c;没有的需要自定义
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
from django import forms
from django.core.exceptions import ValidationErrordef mobile_validate(value):mobile_re &#61; re.compile(r&#39;^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$&#39;)if not mobile_re.match(value):raise ValidationError(&#39;手机号码格式错误&#39;)class PublishForm(forms.Form):user_type_choice &#61; ((0, u&#39;普通用户&#39;),(1, u&#39;高级用户&#39;),)user_type &#61; forms.IntegerField(widget&#61;forms.widgets.Select(choices&#61;user_type_choice,attrs&#61;{&#39;class&#39;: "form-control"}))title &#61; forms.CharField(max_length&#61;20,min_length&#61;5,error_messages&#61;{&#39;required&#39;: u&#39;标题不能为空&#39;,&#39;min_length&#39;: u&#39;标题最少为5个字符&#39;,&#39;max_length&#39;: u&#39;标题最多为20个字符&#39;},widget&#61;forms.TextInput(attrs&#61;{&#39;class&#39;: "form-control",&#39;placeholder&#39;: u&#39;标题5-20个字符&#39;}))memo &#61; forms.CharField(required&#61;False,max_length&#61;256,widget&#61;forms.widgets.Textarea(attrs&#61;{&#39;class&#39;: "form-control no-radius", &#39;placeholder&#39;: u&#39;详细描述&#39;, &#39;rows&#39;: 3}))phone &#61; forms.CharField(validators&#61;[mobile_validate, ],error_messages&#61;{&#39;required&#39;: u&#39;手机不能为空&#39;},widget&#61;forms.TextInput(attrs&#61;{&#39;class&#39;: "form-control",&#39;placeholder&#39;: u&#39;手机号码&#39;}))email &#61; forms.EmailField(required&#61;False,error_messages&#61;{&#39;required&#39;: u&#39;邮箱不能为空&#39;,&#39;invalid&#39;: u&#39;邮箱格式错误&#39;},widget&#61;forms.TextInput(attrs&#61;{&#39;class&#39;: "form-control", &#39;placeholder&#39;: u&#39;邮箱&#39;}))def publish(request):ret &#61; {&#39;status&#39;: False, &#39;data&#39;: &#39;&#39;, &#39;error&#39;: &#39;&#39;, &#39;summary&#39;: &#39;&#39;}if request.method &#61;&#61; &#39;POST&#39;:request_form &#61; PublishForm(request.POST)if request_form.is_valid():request_dict &#61; request_form.clean()print request_dictret[&#39;status&#39;] &#61; Trueelse:error_msg &#61; request_form.errors.as_json()ret[&#39;error&#39;] &#61; json.loads(error_msg)return HttpResponse(json.dumps(ret))