Django之常用模块
分页模块
原始分页器
视图函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | from django.shortcuts import render from django.http import HttpResponse from django.urls import reverse from django.utils.safestring import mark_safe # Create your views here. player_list = [ 'Scholes' , 'Keane' , 'Beckham' , 'Giggs' ] def index(request): name = 'Beckham' return render(request, 'index.html' , { 'name' : name}) test_list = [] for i in range ( 100 ): test_list.append(i) def user_list(request): # 当前页码 current_page = request.GET.get( 'p' , 1 ) # 由于获取的是字符串,所以需要转换成数字 current_page = int (current_page) # 算页数 count, remainder = divmod ( len (test_list), 10 ) if remainder: count + = 1 # 创建页码html page_html_list = [] for i in range ( 1 , count + 1 ): if i = = current_page: page_html_list.append( '<a class="pagination active" href="/user_list/?p=%s" rel="noopener nofollow">%s</a>' % (i, i)) else : page_html_list.append( '<a class="pagination" href="/user_list/?p=%s" rel="noopener nofollow">%s</a>' % (i, i)) page_str = ''.join(page_html_list) page_str = mark_safe(page_str) current_page = int (current_page) list_start = (current_page - 1 ) * 10 list_end = current_page * 10 show_list = test_list[list_start:list_end] return render(request, 'user_list.html' , { 'test_list' : show_list, 'page_str' : page_str}) |
HTML
1 2 3 4 5 6 7 8 9 10 11 12 | <title>Title< / title> <style>.pagination { padding: 5px ; background - color: rgba( 0 , 255 , 255 , 1 ); color: rgba( 255 , 255 , 255 , 1 ); margin: 5px } .pagination.active { background - color: rgba( 255 , 0 , 0 , 1 ) }< / style> <ul> { % for item in test_list % } <li>{{ item }}< / li> { % endfor % } < / ul> {{ page_str }} |
知识点:
1、在网页上为了安全,如果是从后端返回的渲染模板的字符串带有Html的标签,则默认情况下是当作字符串处理,而不解释为Html标签。
2、解决办法:
(1)、模板中使用safe函数
(2)、在后端处理
利用cookie定制分页
可以通过Cookie实现每个分页显示的条目数量
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | < title >Title</ title > < style >.pagination { padding: 5px; background-color: rgba(0, 255, 255, 1); color: rgba(255, 255, 255, 1); margin: 5px } .pagination.active { background-color: rgba(255, 0, 0, 1) }</ style > < ul > {% for item in items_show_list %} < li >{{ item }}</ li > {% endfor %} </ ul > {{ page_str }} < form style="display: inline-block" action="/user_list/" method="get"> < input type="text" name="p" style="width: 50px"> < input type="submit" value="跳转"> </ form > < select name="sel_val"> < option value="10">10</ option > < option value="20">20</ option > < option value="30">30</ option > </ select > |
视图函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | from django.shortcuts import render, redirect from django.http import HttpResponse from django.urls import reverse from django.utils.safestring import mark_safe import datetime # Create your views here. player_list = [ 'Scholes' , 'Keane' , 'Beckham' , 'Giggs' ] class Pagination( object ): def __init__( self , itemcount_per_page, item_count, current_page = 1 , pagination_show_count = 11 ): self .itemcount_per_page = itemcount_per_page self .item_count = item_count self .current_page = current_page self .pagination_show_count = pagination_show_count @property def start( self ): """计算条目起始索引""" return ( self .current_page - 1 ) * self .itemcount_per_page @property def end( self ): """计算条目结束索引""" return self .current_page * self .itemcount_per_page @property def page_count( self ): """计算总页数""" total_count, remainder = divmod ( self .item_count, self .itemcount_per_page) if remainder: total_count + = 1 return total_count def make_page_str( self ): """生成页码html""" # 如果总页数少于要显示的页数 page_str_list = [] if self .page_count < = self .pagination_show_count: pagination_start = 1 pagination_end = self .page_count + 1 else : # 如果是页码在中间的情况 pagination_start = self .current_page - ( self .pagination_show_count - 1 ) / 2 pagination_end = self .current_page + ( self .pagination_show_count + 1 ) / 2 # 页码在头 if self .current_page < = ( self .pagination_show_count + 1 ) / 2 : pagination_start = 1 pagination_end = self .pagination_show_count + 1 # 页码在后面 if self .current_page + ( self .pagination_show_count - 1 ) / 2 > = self .page_count: pagination_end = self .page_count + 1 pagination_start = self .page_count - self .pagination_show_count + 1 if self .current_page ! = 1 : page_str_list.append( '<a class="pagination" href="/user_list/?p=%s" rel="noopener nofollow">上一页</a>' % ( self .current_page - 1 )) else : page_str_list.append( '<a class="pagination">上一页</a>' ) for i in range ( int (pagination_start), int (pagination_end)): if i = = self .current_page: page_str_list.append( '<a class="pagination active" href="/user_list/?p=%s" rel="noopener nofollow">%s</a>' % (i, i)) else : page_str_list.append( '<a class="pagination" href="/user_list/?p=%s" rel="noopener nofollow">%s</a>' % (i, i)) if self .current_page ! = self .page_count: page_str_list.append( '<a class="pagination" href="/user_list/?p=%s" rel="noopener nofollow">下一页</a>' % ( self .current_page + 1 )) else : page_str_list.append( '<a class="pagination">下一页</a>' ) page_str = ''.join(page_str_list) page_str = mark_safe(page_str) return page_str items_list = [] for i in range ( 200 ): items_list.append(i) def user_list(request): # 当前页码 current_page = request.GET.get( 'p' , 1 ) # 由于获取的是字符串,所以需要转换成数字 current_page = int (current_page) # 每页显示的条目数 itemcount_per_page = request.COOKIES.get( 'items_count_per_page' , 10 ) itemcount_per_page = int (itemcount_per_page) # 显示的页码数 pagination_show_count = 11 page_obj = Pagination(itemcount_per_page, len (items_list), current_page, pagination_show_count) html_str = page_obj.make_page_str() items_show_list = items_list[page_obj.start:page_obj.end] return render(request, 'user_list.html' , { 'items_show_list' : items_show_list, 'page_str' : html_str}) auth_db = { 'Treelight' : 'abc123' } def login(request): # 处理get请求 if request.method = = 'GET' : return render(request, 'login.html' ) # 处理post请求 else : username = request.POST.get( 'username' , None ) password = request.POST.get( 'pwd' , None ) # 获取用户名是否存在的结果 db_pwd = auth_db.get(username) # 用户名不存在 if not db_pwd: return redirect( '/login/' ) # 用户存在 else : # 密码正确 if password = = db_pwd: res = redirect( '/index/' ) now = datetime.datetime.utcnow() expire = now + datetime.timedelta(seconds = 10 ) res.set_cookie( 'auth_code' , username, expires = expire) return res # 密码错误 else : return redirect( '/login/' ) def index(request): username = request.COOKIES.get( 'auth_code' ) if not username: return redirect( '/login/' ) return render(request, 'index.html' , { 'username' : username}) |
但以上的太麻烦了,所以Django提供了分页模块,以方便当数据量很大的时候,进行分布处理。
初次使用
使用步骤
一、导入
1 | from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger |
二、实例化
paginator = Paginator(<分页对象列表>, <每页的条目数量>) # 生成一个Paginator对象
三、使用page方法
page = paginator.page(<页码>):显示对应页码条目,并且生成一个page类
概述
django提供的分布模块,主要是用到两个类:Paginator和Page类,有各自的属性和方法
Paginator
下面介绍此类的常用属性和方法。
一、创建方法
paginator = Paginator(<分页对象列表>, <每页的条目数量>) # 生成一个Paginator对象
二、常用属性
Paginator对象.count:分页对象列表的个数
Paginator对象.num_pages:页码数
Paginator对象.page_range:页码范围
Page
一、创建方法
page = paginator对象.page(<页码>)
二、常用方法
page对象.next_page_number() # 获取下一页的页码
page对象.previous_page_number() # 获取上一页的页码
page对象.has_next() # 是否有下一页
page对象.has_previous() # 是否有上一页
三、分页例子
此例子主要是演示分页器的使用,主要功能
1、点击页码跳转到相应的页码
2、有上一页、下一页的功能
3、如果页码数量多,则会出现省略号显示
视图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | from django.shortcuts import render from django.http import HttpResponse from app01 import models from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger # Create your views here. def index(request): books = models.Book.objects. all () items_per_page = 6 paginator = Paginator(books, items_per_page) pagination_num = 11 # 显示的页码个数 try : num = int (request.GET.get( 'p' , 1 )) # 获取要显示的页码数 # print(paginator.count) # print(paginator.page_range) page = paginator.page(num) except Exception as e: num = 1 page = paginator.page(num) page_range = paginator.page_range if pagination_num < paginator.count: if num < = (pagination_num + 1 ) / 2 : # 要显示的页码靠前 page_range = page_range[:pagination_num] flag = 1 # 用于给模板渲染 elif paginator.num_pages - num < (pagination_num + 1 ) / 2 : # 要显示的页码靠后 page_range = page_range[paginator.num_pages - pagination_num:] flag = 2 else : start = int (num - (pagination_num - 1 ) / 2 - 1 ) end = int (num + (pagination_num - 1 ) / 2 ) page_range = page_range[start:end] # 显示的页码在中间 flag = 3 # print(page.next_page_number()) # print(page.previous_page_number()) # print(page.has_next()) # print(page.has_previous()) return render(request, 'index.html' , locals ()) def add(request): books = [] for i in range ( 100 ): book = models.Book(name = 'Book' + str (i), price = 30 + i * i) books.append(book) models.Book.objects.bulk_create(books) return HttpResponse( 'OK' ) |
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | < title >Title</ title > < ul > {% for book in page %} < li >{{ book.name }}</ li > {% endfor %} </ ul > < nav aria-label="Page navigation"> < ul class="pagination"> {% if page.has_previous %} < li > < a href="/?p={{ page.previous_page_number }}" aria-label="Previous" rel="noopener nofollow"> 上一页 </ a > </ li > {% else %} < li class="disabled"> < a aria-label="Previous"> 上一页 </ a > </ li > {% endif %} {% if flag == 2 or flag == 3 %} < li >< a href="/?p=1" rel="noopener nofollow">1</ a ></ li > < li >< a style="border-top: 0; border-right: 0; border-bottom: 0; border-left: 1px solid rgba(221, 221, 221, 1)">......</ a ></ li > {% endif %} {% for i in page_range %} {% if i == num %} < li class="active">< a href="/?p={{ i }}" rel="noopener nofollow">{{ i }}</ a ></ li > {% else %} < li >< a href="/?p={{ i }}" rel="noopener nofollow">{{ i }}</ a ></ li > {% endif %} {% endfor %} {% if flag == 1 or flag == 3 %} < li >< a style="border-top: 0; border-right: 0; border-bottom: 0; border-left: 1px solid rgba(221, 221, 221, 1)">......</ a ></ li > < li >< a href="/?p={{ paginator.num_pages }}" rel="noopener nofollow">{{ paginator.num_pages }}</ a ></ li > {% endif %} {% if page.has_next %} < li > < a href="/?p={{ page.next_page_number }}" aria-label="Next" rel="noopener nofollow"> 下一页 </ a > </ li > {% else %} < li class="disabled"> < a href="#" aria-label="Next" rel="noopener nofollow"> 下一页 </ a > </ li > {% endif %} </ ul > </ nav > |
认证系统----Cookie和Session
前叙
A用户登录淘宝时,会在“购物车”看到A的购物记录。B登录,则看到自己的记录。服务器好像能智能识别并且记住用户,返回用户相对应的数据,而且不用每一次连接都需要输入用户名密码。但Http协议是一种无状态的协议,它不能保存用户的相关信息。那么以上所说的淘宝的登陆是怎么样实现的?这就需要Cookie和Session这两个东西。
Cookie
什么是Cookie?这是一个存放在客户端浏览器的容器。存放什么呢?当你在登录页面输入用户名密码时,点提交,会带着Cookie过去,服务器会验证。如果验证通过,服务器会返回一个键值对,键是sessionID,而值是随机生成的一串字符串。然后把这个键值对发给客户端浏览器,客户端浏览器则把这个键值对保存在Cookie里。下一次访问服务器时,则带着Cookie访问。服务器则对Cookie进行验证。Cookie除了可放登陆信息,也可以把其他的个人信息发给服务器处理。
Session
什么是Session?其实是一个存放在服务器端的容器,这又是存放什么呢?在上述Cookie中已经说过,浏览器把用户名密码发送,服务器如果验证通过则会生成键值对返回给客户端。但第二次浏览器带着Cookie过来时,服务器又是怎么样验证?其实第一次登陆时,服务器会把生成的Cookie随机字符串保存在session中。这样,以后浏览器访问时,可以把浏览器的Cookie中sessionID值拿来与保存在Session中的键值对进行比对,进行验证。这个Session默认会保存在django_session的数据库中,字段包含session_id、session_data、过期时间。
注意:对于同一个服务器,同一个浏览器只保存一个用户的信息。
一般认证流程
Cookie的操作
获取Cookie
1 2 3 4 5 6 | request.COOKIES[ 'key' ] request.get_signed_cookie(key, default = RAISE_ERROR, salt = '', max_age = None ) #参数: default: 默认值 salt: 加密盐 max_age: 后台控制过期时间 |
设置Cookie
1 2 3 4 | rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect() rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt = '加密盐' ,...) |
注销
注销很简单,可把session或cookie删除
1 2 3 4 | request.session.flush() # 删除session # 删除cookie rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect() rep.delete_cookie(键) |
Session操作
1 2 3 4 | request.session[ 'is_login' ] = True # 在数据库生成一个随机session_key,并且设置一个键为is_login值为True的字典保存在此key中,key同时返回给客户端 request.session.flush() # 注销,把数据库记录清空 request.session.session_key # 获取session_key request.session.delete(session_key) # 在表django_session中删除对应的session,但如果参数为None,则删除自己的。 |
用户认证的例子,功能如下:
1、验证登录
2、注销
3、使用了装饰器认证
views
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | from django.shortcuts import render, redirect from app01 import models # Create your views here. def auth(func): """ 认证装饰器 :param func: :return: """ def inner(request, * args, * * kwargs): if not request.session.get( 'is_login' ): return redirect( '/login/' ) res = func(request, * args, * * kwargs) return res return inner @auth def index(request): # if not request.session.get('is_login'): # 上面的语句django帮我们做了两件事 # 1、从客户端发过来的字典获取键"sessionID"的值 # 2、然后在对比session的数据:以上面的值作为键,获取此用户的信息(字典的形式),判断此用户是否有is_login的键 # return redirect('/login/') name = request.session[ 'username' ] return render(request, 'index.html' , locals ()) def login(request): if request.method = = 'POST' : username = request.POST.get( 'username' ) pwd = request.POST.get( 'pwd' ) users = models.UserInfo.objects. filter (username = username, pwd = pwd) if users: # 用户名密码正确 request.session[ 'is_login' ] = True # 上面的语句django帮我们做了两件事 # 1、随机生成一个{'sessionID:<值>}键值对,发送给客户端 # 2、保存一个键值对在服务器的session,形式为{<上面的值>:{'is_login':True'}},默认保存 # 在数据库中的django_session request.session[ 'username' ] = username return redirect( '/index/' ) else : return redirect( '/login/' ) return render(request, 'login.html' ) def log_out(request): request.session.flush() # 相当于models.django_session.filter(session_id = <此次会话键为sessionID的值>).delete() # 把数据库记录清空 return redirect( '/login/' ) @auth # 使用装饰器 def add_book(request): return render(request, 'add_book.html' ) |
add_book.html
1 2 3 4 5 | < title >Title</ title > < h1 >这是一个添加书本信息的页面</ h1 > |
index.html
1 2 3 4 5 6 | < title >Title</ title > < h3 >欢迎你,{{ name }}</ h3 > < a href="/logout/" rel="noopener nofollow">注销</ a > |
login.html
Title
auth模块
Django中已经内置了认证模块供我们使用。原理是一样的,相比上面的例子,不同的有以下两点:
1、auth模块已经内置了认证功能,简化了我们的操作。
2、auth模块不需要新增数据库,我们只需要使用django自带的数据表auth_user即可
导入
from django.contrib import auth # 用于认证模块
from django.contrib.auth.models import User # 提供的auth_user数据库接口
auth模块的属性、方法
1 2 3 4 5 6 7 8 | # 属性 request.user.is_authenticated # 判断请求用户是否已经验证 # 方法 auth.logout(request) # 注销用户 auth.authenticate(username = username, password = password) # 验证用户,验证通过则返回用户类; # 注意:验证的用户需用用models.<类名>.objects.create_user()创建,会对密码加密 auth.login(request, user) # 把用户信息写进session User.objects.create_user(username = username, password = password) # 通过User接口访问auth_session表,创建用户 |
例子
此例子可实现注册、登录、注销的功能
视图函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | from django.shortcuts import render, redirect from django.contrib import auth # 用户认证模块 from django.contrib.auth.models import User # 数据表auth_user的接口 # Create your views here. def index(request): if request.user.is_authenticated: # 判断是否已经验证 return render(request, 'index.html' ) return redirect( '/login/' ) def log_out(request): auth.logout(request) # 相当于request.session.flush() return redirect( '/login/' ) def login(request): if request.method = = 'POST' : username = request.POST.get( 'username' ) password = request.POST.get( 'password' ) user = auth.authenticate(username = username, password = password) # 对用户名密码进行认证,认证通过则会返回值 if user: auth.login(request, user) # 把记录写到django_session return redirect( '/' ) else : return redirect( '/login/' ) return render(request, 'login.html' ) def reg(request): if request.method = = 'POST' : username = request.POST.get( 'username' ) password = request.POST.get( 'password' ) User.objects.create_user(username = username, password = password) # 把用户记录到auth_user表中 return redirect( '/login/' ) return render(request, 'reg.html' ) |
reg.html
1 2 3 4 5 6 7 8 9 10 | <title>Title< / title> <form method = "post" action = "/reg/" > { % csrf_token % } <p>用户名 < input type = "text" name = "username" >< / p> <p>密码 < input type = "password" name = "password" >< / p> < input type = "submit" > < / form> |
login.html
1 2 3 4 5 6 7 8 9 10 11 | <title>Title< / title> <form method = "post" action = "/login/" > { % csrf_token % } <p>用户名 < input type = "text" name = "username" >< / p> <p>密码 < input type = "password" name = "password" >< / p> < input type = "submit" value = "登录" > <a href = "/reg/" rel = "noopener nofollow" >< input type = "button" value = "注册" >< / a> < / form> |
index.html
1 |
auth_user表增加字段
auth模块使用的是Django提供给我们的auth_user表,但现在我们需要在此表新增一个tel字段,怎么办呢?按以下步骤即可完成:
步骤一、在models创建一个类,注意需要继承一个AbstractUser类,而且此类的字段只需要写新增的字段即可。
步骤二、在settings.py中加上以下配置
AUTH_USER_MODEL = '<App名>.<model名>'
步骤三、python manage.py makemigrations
python manage.py migrate
利用Ajax实现用户注册、登录、注销
视图函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | from django.shortcuts import render, redirect from django.http import HttpResponse from django.contrib import auth from django.contrib.auth.models import User # Create your views here. def index(request): if request.user.is_authenticated: return render(request, 'index.html' ) return redirect( '/login/' ) def login(request): res = { 'is_login' : False } if request.method = = 'POST' : username = request.POST.get( 'username' ) password = request.POST.get( 'password' ) user = auth.authenticate(username = username, password = password) if user: auth.login(request, user) res[ 'is_login' ] = True import json return HttpResponse(json.dumps(res)) return render(request, 'login.html' ) def reg(request): if request.method = = 'POST' : username = request.POST.get( 'username' ) password = request.POST.get( 'password' ) User.objects.create_user(username = username, password = password) import json return HttpResponse(json.dumps({ 'res' : True })) # 由于需要和js交换,所以需要把字典序列化成json标准格式 return render(request, 'reg.html' ) def log_out(request): auth.logout(request) return redirect( '/login/' ) |
index.html
1 |
reg.html
1 |
login.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | Title < script src="/static/dist/js/bootstrap.js"></ script > {% csrf_token %} 这是一个登录页面 用户名 密码 登录 注册 |
Form模块
作用
在模板中生成各种Form表单标签,然后可以验证各种在标签中的输入,比如注册时,用户名的长度是否合适。下面以注册为例说明怎么使用Form表单验证。
Form表单类
Form表单的创建
在视图函数中定义一个继承Form的类,建议放在py文件里
1 2 3 4 5 6 7 8 9 | class RegForm(forms.Form): # 必须继承此类 # 定义表单 username = forms.CharField(min_length = 8 , error_messages = { 'required' : '不能为空' }) # 定义了字段的规则,以下类同,同时也定义了错误显示的信息 password = forms.CharField(min_length = 2 , max_length = 12 , widget = widgets.PasswordInput()) # 定义了标签的属性 repeat_password = forms.CharField(min_length = 2 , max_length = 12 , widget = widgets.PasswordInput()) email = forms.EmailField() |
此类实例化后,可渲染给模板使用。例子如下:
1 2 3 | 用户名 {{ reg_form.username }}{{ reg_form.errors.username. 0 }} {{ form.username.label }} # 利用表单中字段对应的label参数渲染,如username = fields.CharField(label='用户名:') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from django.shortcuts import render, redirect from django import forms from django.forms import widgets, fields class FM(forms.Form): """验证表单""" # 此名称必须要与需要验证的html中的name属性对应 user = fields.CharField(error_messages = { 'required' : '用户名不能为空' }, widget = widgets.Textarea(attrs = { 'class' : 'c1' })) pwd = fields.CharField( min_length = 6 , max_length = 12 , error_messages = { 'required' : '密码不能为空' , 'min_length' : '密码至少需要6位' , 'max_length' : "密码不多于12位" } ) email = fields.EmailField(error_messages = { 'required' : '邮箱不能为空' }) |
内置的字段参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | Field required = True , 是否允许为空 widget = None , HTML插件 label = None , 用于生成Label标签或显示内容 initial = None , 初始值 help_text = '', 帮助信息(在标签旁边显示) error_messages = None , 错误信息 { 'required' : '不能为空' , 'invalid' : '格式错误' } show_hidden_initial = False , 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) validators = [], 自定义验证规则 localize = False , 是否支持本地化 disabled = False , 是否可以编辑 label_suffix = None Label内容后缀 CharField(Field) max_length = None , 最大长度 min_length = None , 最小长度 strip = True 是否移除用户输入空白 IntegerField(Field) max_value = None , 最大值 min_value = None , 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value = None , 最大值 min_value = None , 最小值 max_digits = None , 总长度 decimal_places = None , 小数位长度 BaseTemporalField(Field) input_formats = None 时间格式化 DateField(BaseTemporalField) 格式: 2015 - 09 - 01 TimeField(BaseTemporalField) 格式: 11 : 12 DateTimeField(BaseTemporalField)格式: 2015 - 09 - 01 11 : 12 DurationField(Field) 时间间隔: % d % H: % M: % S. % f ... RegexField(CharField) regex, 自定制正则表达式 max_length = None , 最大长度 min_length = None , 最小长度 error_message = None , 忽略,错误信息使用 error_messages = { 'invalid' : '...' } EmailField(CharField) ... FileField(Field) allow_empty_file = False 是否允许空文件 ImageField(FileField) ... 注:需要PIL模块,pip3 install Pillow 以上两个字典使用时,需要注意两点: - form表单中 enctype = "multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices = (), 选项,如:choices = (( 0 , '上海' ),( 1 , '北京' ),) required = True , 是否必填 widget = None , 插件,默认select插件 label = None , Label内容 initial = None , 初始值 help_text = '', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label = "---------" , # 默认空显示内容 to_field_name = None , # HTML中value的值对应的字段 limit_choices_to = None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value = '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每一个值进行一次转换 empty_value = '' 空值的默认值 ComboField(Field) fields = () 使用多个验证,如下:即验证最大长度 20 ,又验证邮箱格式 fields.ComboField(fields = [fields.CharField(max_length = 20 ), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats = None , 格式列表:[ '%Y--%m--%d' , '%m%d/%Y' , '%m/%d/%y' ] input_time_formats = None 格式列表:[ '%H:%M:%S' , '%H:%M:%S.%f' , '%H:%M' ] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match = None , 正则匹配 recursive = False , 递归下面的文件夹 allow_files = True , 允许文件 allow_folders = False , 允许文件夹 required = True , widget = None , label = None , initial = None , help_text = '' GenericIPAddressField protocol = 'both' , both,ipv4,ipv6支持的IP格式 unpack_ipv4 = False 解析ipv4地址,如果是::ffff: 192.0 . 2.1 时候,可解析为 192.0 . 2.1 , PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型 ... |
widget插件参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | TextInput( Input ) NumberInput(TextInput) EmailInput(TextInput) URLInput(TextInput) PasswordInput(TextInput) HiddenInput(TextInput) Textarea(Widget) DateInput(DateTimeBaseInput) DateTimeInput(DateTimeBaseInput) TimeInput(DateTimeBaseInput) CheckboxInput Select NullBooleanSelect SelectMultiple RadioSelect CheckboxSelectMultiple FileInput ClearableFileInput MultipleHiddenInput SplitDateTimeWidget SplitHiddenDateTimeWidget SelectDateWidget |
常用的选择插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | # 单radio,值为字符串 # user = fields.CharField( # initial=2, # widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) # ) # 单radio,值为字符串 # user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), # initial=2, # widget=widgets.RadioSelect # ) # 单select,值为字符串 # user = fields.CharField( # initial=2, # widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) # ) # 单select,值为字符串 # user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), # initial=2, # widget=widgets.Select # ) # 多选select,值为列表 # user = fields.MultipleChoiceField( # choices=((1,'上海'),(2,'北京'),), # initial=[1,], # widget=widgets.SelectMultiple # ) # 单checkbox # user = fields.CharField( # widget=widgets.CheckboxInput() # ) # 多选checkbox,值为列表 # user = fields.MultipleChoiceField( # initial=[2, ], # choices=((1, '上海'), (2, '北京'),), # widget=widgets.CheckboxSelectMultiple # ) |
但以上选择插件使用时,比如Select插件,只会静态生成一个选择列表,当有新数据添加到数据库时,此列表不会自动更新。解决办法是重写构造函数,如下:
插件使用注意事项
使用日期插件
1 2 3 | birth_date = forms.DateField( widget = fwidgets.DateInput(attrs = { 'type' : 'date' , 'class' : 'form-control' }) ) |
Form表单类的属性与方法
1 2 3 4 5 6 7 | # 实例化 reg - form = RegForm() # 实例化,参数为空 reg_form = RegForm(request.POST) # 把要验证的数据放进表单里 reg_form.is_valid() # 把表单数据分成两部分:干净数据(clean_data)和错误数据,然后返回是否所有数据通过验证 reg_form.cleaned_data # 获取干净数据 reg_form.errors # 返回错误字典,形如:{'username':[errormsg1、errormsg2], 'password':...} reg_form.add_error(字段名,错误提示) # 在字段名中添加错误信息 |
页面注册验证例子
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | Title < script src="/static/jquery.cookie.min.js"></ script > < script src="/static/dist/js/bootstrap.js"></ script > < style > .error { color: red, } </ style > >这是一个注册页面 {% csrf_token %} 用户名 {{ reg_form.username }}{{ reg_form.errors.username.0 }} 密码 {{ reg_form.password }}{{ reg_form.errors.password.0 }} 确认密码 {{ reg_form.repeat_password }}{{ reg_form.errors.repeat_password.0 }} 邮箱 {{ reg_form.email }}{{ reg_form.errors.email.0 }} |
表单创建
1 2 3 4 5 6 7 8 9 10 11 12 13 | from django import forms # 导入forms模块 from django.forms import widgets # 导入插件 class RegForm(forms.Form): # 必须继承此类 # 定义表单 username = forms.CharField(min_length = 8 , error_messages = { 'required' : '不能为空' }) # 定义了字段的规则,以下类同,同时也定义了错误显示的信息 password = forms.CharField(min_length = 2 , max_length = 12 , widget = widgets.PasswordInput()) # 定义了标签的属性 repeat_password = forms.CharField(min_length = 2 , max_length = 12 , widget = widgets.PasswordInput()) email = forms.EmailField() |
视图函数
1 2 3 4 5 6 7 8 9 10 11 12 13 | def reg(request): if request.method = = 'POST' : reg_form = forms.RegForm(request.POST) # 实例化表单,传了从客户端返回来的数据 if reg_form.is_valid(): # 把表单数据分成两部分:干净数据(clean_data)和错误数据,然后返回是否所有数据通过验证 print ( 'is_valid' ) print (reg_form.cleaned_data) else : print (reg_form.errors[ 'username' ]) # 返回错误字典,形如:{'username':[<errormsg>、<errormsg2>], 'password':...} return render(request, 'reg.html' , { 'reg_form' : reg_form}) # 此类渲染给模板 reg_form = forms.RegForm() # 实例化表单类,参数为空 return render(request, 'reg.html' , { 'reg_form' : reg_form}) # 把此类渲染给模板 |
Form表单的钩子
表单提供的验证规则不一定够我们使用,这时可自定义规则,需要使用到表单中的钩子,分为局部钩子和全局钩子
局部钩子
使用方法:
一、引入:from django.core.exception import ValidationError
二、在原来表单类的基础上新增一个方法def clean_字段名,此名称不能乱起。
三、添加验证规则。一般验证通过返回self.clean_data.get(字段名);不能过则ValidationError(错误信息)
局部钩子例子
自定义验证规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | django import forms # 导入forms模块 from django.forms import widgets # 导入插件 from django.core.exceptions import ValidationError class RegForm(forms.Form): # 必须继承此类 # 定义表单 username = forms.CharField(min_length = 8 , error_messages = { 'required' : '不能为空' }) # 定义了字段的规则,以下类同,同时也定义了错误显示的信息 password = forms.CharField(min_length = 2 , max_length = 12 , widget = widgets.PasswordInput()) # 定义了标签的属性 repeat_password = forms.CharField(min_length = 2 , max_length = 12 , widget = widgets.PasswordInput()) email = forms.EmailField() def clean_username( self ): # 定义钩子函数,此处可添加验证规则,比如用户名不能全为数字 # 此函数的名字必须是clean_字段名 if not self .cleaned_data.get( 'username' ).isdigit(): # 如果不是全数字,返回干净的数据 return self .cleaned_data.get( 'username' ) else : raise ValidationError( '不能全是数字' ) # 返回错误信息 |
全局钩子
一般用在两个字段需要比较时,比如说注册需要两次输入的密码一致时 使用方法: 一、引入:from django.core.exception import ValidationError 二、在原来表单类的基础上新增一个方法def clean,此名称不能乱起。 三、添加验证规则。一般验证通过返回self.clean_data;不能过则ValidationError(错误信息)两次密码一致例子
Form表单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | django import forms # 导入forms模块 from django.forms import widgets # 导入插件 from django.core.exceptions import ValidationError class RegForm(forms.Form): # 必须继承此类 # 定义表单 username = forms.CharField(min_length = 8 , error_messages = { 'required' : '不能为空' }) # 定义了字段的规则,以下类同,同时也定义了错误显示的信息 password = forms.CharField(min_length = 2 , max_length = 12 , widget = widgets.PasswordInput()) # 定义了标签的属性 repeat_password = forms.CharField(min_length = 2 , max_length = 12 , widget = widgets.PasswordInput()) email = forms.EmailField() def clean_username( self ): # 定义钩子函数,此处可添加验证规则,比如用户名不能全为数字 # 此函数的名字必须是clean_字段名 if not self .cleaned_data.get( 'username' ).isdigit(): # 如果不是全数字 return self .cleaned_data.get( 'username' ) else : raise ValidationError( '不能全是数字' ) def clean( self ): # 定义一个判断两次密码输入是否一致的规则 if self .cleaned_data.get( 'password' ) = = self .cleaned_data.get( 'repeat_password' ): return self .cleaned_data # 此处返回和局部钩子不同 else : raise ValidationError( '两次密码不一致' ) |
注意,由于获取全局钩子定义的错误信息是需要使用reg_form.errors.get('all'),键是__all__,在模板里是不支持的,所以需要在视图函数中先使用键__all__获取错误信息,再渲染。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」