Python 14:Django
1、web框架
2、Django基本配置
3、视图
4、路由系统
5、分页
6、ORM操作
7、ajax
8、文件上传
9、Model操作
10、Form组件验证
11、ModelForm操作
12、cookie
13、session
14、跨站请求伪造
15、中间件
16、缓存
17、信号
18、动态验证码
19、KindEditor
20、组合搜索
21、JSONP跨域请求
一、web框架
1、web框架本质
对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端
1 import socket 2 def handle_request(client): 3 buf = client.recv(1024) 4 client.send("HTTP/1.1 200 OK\r\n\r\n".encode('utf-8')) 5 client.send("Hello!!".encode('utf-8')) 6 def main(): 7 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 sock.bind(('localhost',8000)) 9 sock.listen(5) 10 while True: 11 connection, address = sock.accept() 12 handle_request(connection) 13 connection.close() 14 if __name__ == '__main__': 15 main()
上述通过socket来实现了其本质,而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。
WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦
python标准库提供的独立WSGI服务器称为wsgiref
1 from wsgiref.simple_server import make_server 2 3 def RunServer(environ, start_response): 4 #environ 客户端发来的所有数据 5 #start_response 封装要返回给用户的数据,响应头 6 start_response('200 OK', [('Content-Type', 'text/html')]) 7 #返回的内容 8 return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] 9 10 if __name__ == '__main__': 11 httpd = make_server('', 8000, RunServer) 12 print("Serving HTTP on port 8000...") 13 httpd.serve_forever()
2、自定义web框架
最简单的web框架
1 from wsgiref.simple_server import make_server 2 3 def handle_index(): 4 return ['<h1>Hello,Index!</h1>'.encode('utf-8')] 5 def handle_date(): 6 return ['<h1>Hello,Date!</h1>'.encode('utf-8')] 7 8 9 def RunServer(environ, start_response): 10 #environ 客户端发来的所有数据 11 #start_response 封装要返回给客户的数据,响应头状态 12 start_response('200 OK', [('Content-Type', 'text/html')]) 13 14 current_url = environ['PATH_INFO'] 15 if current_url == '/index': 16 return handle_index() 17 elif current_url == '/date': 18 return handle_date() 19 else: 20 #返回的内容 21 return ['<h1>404</h1>'.encode('utf-8')] 22 23 if __name__ == '__main__': 24 httpd = make_server('', 8000, RunServer) 25 print("Serving HTTP on port 8000...") 26 httpd.serve_forever()
1 from wsgiref.simple_server import make_server 2 3 def handle_index(): 4 return ['<h1>Hello,Index!</h1>'.encode('utf-8')] 5 def handle_date(): 6 return ['<h1>Hello,Date!</h1>'.encode('utf-8')] 7 8 URL_DICT= { 9 '/index':handle_index, 10 '/date':handle_date 11 } 12 def RunServer(environ, start_response): 13 #environ 客户端发来的所有数据 14 #start_response 封装要返回给客户的数据,响应头状态 15 start_response('200 OK', [('Content-Type', 'text/html')]) 16 current_url = environ['PATH_INFO'] 17 func = None 18 if current_url in URL_DICT: 19 func = URL_DICT[current_url] 20 if func: 21 return func() 22 else: 23 return ['<h1>404</h1>'.encode('utf-8')] 24 # if current_url == '/index': 25 # return handle_index() 26 # elif current_url == '/date': 27 # return handle_date() 28 # else: 29 # #返回的内容 30 # return ['<h1>404</h1>'.encode('utf-8')] 31 if __name__ == '__main__': 32 httpd = make_server('', 8000, RunServer) 33 print("Serving HTTP on port 8000...") 34 httpd.serve_forever()
在上一步骤中,对于所有的login、index均返回给用户浏览器一个简单的字符串,在现实的Web请求中一般会返回一个复杂的符合HTML规则的字符串,所以我们一般将要返回给用户的HTML写在指定文件中,然后再返回;同理也可以将定义的函数和数据库内容放到单独的文件当中。
1 import time 2 def handle_index(): 3 v = str(time.time()) 4 f = open('View/index.html',mode='rb') 5 data = f.read() 6 f.close() 7 data = data.replace(b'@uuu', v.encode('utf-8')) 8 return [data,] 9 10 def handle_date(): 11 return ['<h1>Hello,Date!</h1>'.encode('utf-8')]
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <h1>INDEX @uuu</h1> </body> </html>
1 from wsgiref.simple_server import make_server 2 from Controller import account 3 4 URL_DICT= { 5 '/index':account.handle_index, 6 '/date':account.handle_date 7 } 8 9 def RunServer(environ, start_response): 10 #environ 客户端发来的所有数据 11 #start_response 封装要返回给客户的数据,响应头状态 12 start_response('200 OK', [('Content-Type', 'text/html')]) 13 current_url = environ['PATH_INFO'] 14 func = None 15 if current_url in URL_DICT: 16 func = URL_DICT[current_url] 17 if func: 18 return func() 19 else: 20 return ['<h1>404</h1>'.encode('utf-8')] 21 # if current_url == '/index': 22 # return handle_index() 23 # elif current_url == '/date': 24 # return handle_date() 25 # else: 26 # #返回的内容 27 # return ['<h1>404</h1>'.encode('utf-8')] 28 if __name__ == '__main__': 29 httpd = make_server('', 8000, RunServer) 30 print("Serving HTTP on port 8000...") 31 httpd.serve_forever()
3、web框架
MVC
Model View Controller
数据库 模板文件 业务处理
MTV
Model Template View
数据库 模板文件 业务处理
二、Django基本配置
1、创建Django程序
pip3 install django
添加环境变量C:\Python35\Scripts
创建Django工程:
终端命令:django-admin startproject 【工程名称】(IDE创建Django程序时,本质上都是自动执行上述命令)
运行Django功能:python manage.py runserver 127.0.0.1:8001
2、目录详解
django_zz
- django_zz # 对整个程序进行配置
- init
- settings # 配置文件
- url # URL对应关系
- wsgi # 一套接口规则、遵循WSIG规范,上线时不能用自带的wsgi需要(uwsgi + nginx)
- manage.py # 管理Django程序:
- python manage.py
- python manage.py startapp xx
- python manage.py makemigrations
- python manage.py migrate
3、创建app
-python manage.py startapp cmdb
-python manage.py startapp openstack
-python manage.py startapp .......
(代码放在创建的app下的view中)
1 from django.shortcuts import render 2 3 # Create your views here. 4 from django.shortcuts import HttpResponse 5 6 def home(request): 7 return HttpResponse('<h1>CMDB</h1>')
URL对应关系
1 from django.contrib import admin 2 from django.urls import path 3 from cmdb import views 4 5 urlpatterns = [ 6 path('admin/', admin.site.urls), 7 path('h.html/', views.home), 8 ]
app目录:
- migrations 数据修改表结构(Django自动生成)
- admin Django为我们提供的后台管理
- apps 配置当前app
- models ORM,写指定的类 通过命令可以创建数据库结构
- tests 单元测试
- views 业务代码
4、创建完project后做的事情
a.创建app
cd 工程名
python manage.py startapp cmdb
b.配置模板的路径(新版Django自动配置)
project/project/settings.py中
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
c.配置静态文件目录(static)
project/project/settings.py末尾
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
引入css:<link rel="stylesheet" href="/static/commons.css">
d.settings中
找到middlerware
注释:# 'django.middleware.csrf.CsrfViewMiddleware',
e.定义路由规则
在url.py中写对应关系:"login" --> 函数名
path('login', views.login),
path('home', views.home),
f.定义视图函数
app下views.py
def func(request):
# request.method GET / POST
# http://127.0.0.1:8009/home?nid=123&name=alex
# request.GET.get('',None) # 获取请求发来的而数据
# request.POST.get('',None)
# return HttpResponse("字符串")
# return render(request, "HTML模板的路径")
# return redirect('/只能填URL')
from django.shortcuts import render # Create your views here. from django.shortcuts import HttpResponse from django.shortcuts import render from django.shortcuts import redirect def home(request): if request.method == 'POST': #获取用户提交的数据POST请求 u = request.POST.get('username') e = request.POST.get('email') g = request.POST.get('gender') temp = {'username':u,'email':e,'gender':g} USER_LIST.append(temp) # return HttpResponse('<h1>CMDB</h1>') return render(request,'home.html',{'user_list': USER_LIST}) USER_LIST = [ {'username':'zz','email':'qq@qq','gender':'m'} ] # for item in range(20): # temp = {'username':'zz' + str(item),'email':'ee@ee','gender':'m'} # USER_LIST.append(temp) def login(request): #获取用户提交方法 error_msg = '' print(request.method) if request.method == 'POST': #用户通过post提交过来的数据 # user = request.POST['user'] # pwd = request.POST['pwd'] # print(user,pwd) user = request.POST.get('user',None) pwd = request.POST.get('pwd',None) print(user,pwd) if user == 'root' and pwd == '123': return redirect('/home') else: #用户密码不匹配 error_msg = '用户名或密码错误' return render(request,'login.html',{'error_msg':error_msg}) # def login(request): # string = ''' # <form> # <input type='text' /> # </form> # ''' # f = open('templates/login.html','r',encoding='utf-8') # data = f.read() # f.close() # return HttpResponse(data) # return render(request,'login.html')
g.模板语言
1、特殊的模板语言
元组列表直接循环,字典循环不加括号
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <ul> 9 {# {% for item in user_dict.values %}#} 10 {# {% for item in user_dict.keys %}#} 11 {% for k,item in user_dict.items %} 12 <li>{{ k }}:{{ item }}</li> 13 {% endfor %} 14 </ul> 15 16 </body> 17 </html>
1 USER_DICT = { 2 '1':{'name':'root1','email':'root@aaa.com'}, 3 '2':{'name':'root2','email':'root@aaa.com'}, 4 '3':{'name':'root3','email':'root@aaa.com'}, 5 '4':{'name':'root4','email':'root@aaa.com'}, 6 '5':{'name':'root5','email':'root@aaa.com'}, 7 } 8 9 10 11 def index(request): 12 return render(request,'index.html',{'user_dict':USER_DICT})
1 特殊的模板语言 2 3 -- {{ 变量名 }} 4 5 def func(request): 6 return render(request, "index.html", {'current_user': "alex"}) 7 8 9 index.html 10 11 <html> 12 .. 13 <body> 14 <div>{{current_user}}</div> 15 </body> 16 17 </html> 18 19 ====> 最后生成的字符串 20 21 <html> 22 .. 23 <body> 24 <div>alex</div> 25 </body> 26 27 </html> 28 -- For循环 29 def func(request): 30 return render(request, "index.html", {'current_user': "alex", 'user_list': ['alex','eric']}) 31 32 33 index.html 34 35 <html> 36 .. 37 <body> 38 <div>{{current_user}}</div> 39 40 <ul> 41 {% for row in user_list %} 42 43 {% if row == "alex" %} 44 <li>{{ row }}</li> 45 {% endif %} 46 47 {% endfor %} 48 </ul> 49 50 </body> 51 52 </html> 53 54 #####索引################# 55 def func(request): 56 return render(request, "index.html", { 57 'current_user': "alex", 58 'user_list': ['alex','eric'], 59 'user_dict': {'k1': 'v1', 'k2': 'v2'}}) 60 61 62 index.html 63 64 <html> 65 .. 66 <body> 67 <div>{{current_user}}</div> 68 69 <a> {{ user_list.1 }} </a> 70 <a> {{ user_dict.k1 }} </a> 71 <a> {{ user_dict.k2 }} </a> 72 73 </body> 74 75 </html> 76 77 ###### 条件 78 79 def func(request): 80 return render(request, "index.html", { 81 'current_user': "alex", 82 "age": 18, 83 'user_list': ['alex','eric'], 84 'user_dict': {'k1': 'v1', 'k2': 'v2'}}) 85 86 87 index.html 88 89 <html> 90 .. 91 <body> 92 <div>{{current_user}}</div> 93 94 <a> {{ user_list.1 }} </a> 95 <a> {{ user_dict.k1 }} </a> 96 <a> {{ user_dict.k2 }} </a> 97 98 {% if age %} 99 <a>有年龄</a> 100 {% if age > 16 %} 101 <a>老男人</a> 102 {% else %} 103 <a>小鲜肉</a> 104 {% endif %} 105 {% else %} 106 <a>无年龄</a> 107 {% endif %} 108 </body> 109 110 </html>
2、模板继承
母版
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} {% endblock %}</title> <link rel="stylesheet" href="/static/commons.css"/> <style> .pg-header{ height: 48px; background-color: black; color: white; } </style> {# 为子板提供单独css样式#} {% block css %}{% endblock %} </head> <body> <div class="pg-header"> 系统管理 </div> {% block content %} {% endblock %} <script src="/static/js.js"></script> {# 为子板提供单独js样式#} {% block js %} {% endblock %} </body> </html>
子板
{#继承那个模板#} {% extends 'master.html' %} {#替换模板的那个模块#} {% block title %}用户管理{% endblock %} {% block content %} <h1>用户管理</h1> <ul> {% for i in u %} <li>{{ i }}</li> {% endfor %} </ul> {% endblock %} {#设置单独css样式#} {% block css %} <style> body{ background-color: aqua; } </style> {% endblock %}
3、模板导入
模版
<form> <input type="text"/> <input type="submit"/> </form>
导入模板
{#继承那个模板#} {% extends 'master.html' %} {% block content %} <h1>用户管理</h1> <ul> {% for i in u %} <li>{{ i }}</li> {#导入模板#} {% include 'tag.html' %} {% endfor %} </ul> {#导入模板#} {% include 'tag.html' %} {% include 'tag.html' %} {% endblock %}
4、自定义simple_tag
4.1、在某个app下创建templatetags目录
4.2、创建任意.py文件,例:zz.py
4.3、编写函数(template对象名称必须是register)
1 from django import template 2 from django.utils.safestring import mark_safe 3 4 register = template.Library() 5 @register.simple_tag 6 def jj(a1,a2): 7 return a1 + a2 8 9 @register.simple_tag 10 def pp(): 11 return 123
4.4、在settings中配置当前app,不然django无法找到自定义的simple_tag
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', ]
4.5、在使用自定义simple_tag的html文件中导入之前创建的 xx.py 文件名(在顶部)
{% load zz %}
4.6、使用simple_tag
{% 函数名 arg1 arg2%}
{% pp %}
1 {% load zz %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title></title> 7 </head> 8 <body> 9 {{ name }} 10 {{ name|lower }} 11 {% jj 3 7%} 12 {% pp %} 13 </body> 14 </html>
5、自定义filter
函数(最多只能有两个参数)
1 from django import template 2 from django.utils.safestring import mark_safe 3 4 register = template.Library() 5 6 @register.filter 7 def qq(a1,a2): 8 return a1 + a2
使用filter:
{{ '参数1'|函数名:'参数2' }}
filter主要用在if...else判断里,用于if条件
三、视图
1、获取用户请求数据
request.GET
request.POST
request.FILES
PS:
GET:获取数据
POST:提交数据
获取cookie信息
request.cookie
获取用户请求相关信息及请求头
from django.core.handleers.wsgi import WSGIRequest
request.environ
request.environ['HTTP_USER_AGENT']
2、checkbox等多选的内容
request.POST.getlist()
3、上传文件
# 上传文件,form标签做特殊设置:<form action="/login/" method="post" enctype="multipart/form-data">
obj = request.FILES.get('fafafa')
obj.name
f = open(obj.name, mode='wb')
for item in obj.chunks():
f.write(item)
f.close()
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form action="/login/" method="post" enctype="multipart/form-data"> 9 <p> 10 <input type="text" name="user" placeholder="用户名"/> 11 </p> 12 <p> 13 <input type="password" name="pwd" placeholder="密码"/> 14 </p> 15 <p> 16 男:<input type="radio" name="gender" value="1"/> 17 女:<input type="radio" name="gender" value="2"/> 18 你猜:<input type="radio" name="gender" value="3"/> 19 </p> 20 <p> 21 男:<input type="checkbox" name="favor" value="1"/> 22 女:<input type="checkbox" name="favor" value="2"/> 23 你猜:<input type="checkbox" name="favor" value="3"/> 24 </p> 25 <p> 26 <select name="city"> 27 <option value="bj">北京</option> 28 <option value="sh">上海</option> 29 <option value="tj">天津</option> 30 </select> 31 </p> 32 <p> 33 <input type="file" name="fff"/> 34 </p> 35 36 <input type="submit" value="提交"/> 37 38 </form> 39 </body> 40 </html>
1 from django.shortcuts import render,HttpResponse,redirect 2 3 # Create your views here. 4 def index(request): 5 return HttpResponse('index') 6 # def login(request): 7 # if request.method == 'GET': 8 # return render(request,'login.html') 9 # elif request.method == 'POST': 10 # u = request.POST.get('user') 11 # p = request.POST.get('pwd') 12 # if u == 'zz'and p == '123': 13 # return redirect('/index/') 14 # else: 15 # return render(request,'login.html') 16 # else: 17 # return redirect('/index/') 18 # return render(request,'login.html') 19 20 def login(request): 21 if request.method == 'GET': 22 return render(request,'login.html') 23 elif request.method == 'POST': 24 #radio 25 # v = request.POST.get('gender') 26 # print(v) 27 28 #checkbox 29 # v = request.POST.getlist('favor') 30 # print(v) 31 32 # select 33 # v = request.POST.get('city') 34 # print(v) 35 36 #file 37 # v = request.POST.get('fff') 38 # print(v) #只会拿到文件 名�� 39 import os 40 v = request.FILES.get('fff') 41 print(v,type(v),v.name) 42 43 file_path = os.path.join('upload',v.name) 44 f = open(file_path,mode='wb') 45 for i in v.chunks(): 46 f.write(i) 47 f.close() 48 49 50 else: 51 return redirect('/index/') 52 return render(request,'login.html')
1 """s14day19 URL Configuration 2 3 The `urlpatterns` list routes URLs to views. For more information please see: 4 https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 Examples: 6 Function views 7 1. Add an import: from my_app import views 8 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 Class-based views 10 1. Add an import: from other_app.views import Home 11 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 Including another URLconf 13 1. Import the include() function: from django.urls import include, path 14 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 """ 16 from django.contrib import admin 17 from django.urls import path 18 from app01 import views 19 20 urlpatterns = [ 21 path('admin/', admin.site.urls), 22 path('index/', views.index), 23 path('login/', views.login), 24 25 ]
4、FBV & CBV
function base view
url.py
index -> 函数名
view.py
def 函数(request):
...(函数内容)
前面写的都是FBV对应关系
1 """s14day19 URL Configuration 2 3 The `urlpatterns` list routes URLs to views. For more information please see: 4 https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 Examples: 6 Function views 7 1. Add an import: from my_app import views 8 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 Class-based views 10 1. Add an import: from other_app.views import Home 11 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 Including another URLconf 13 1. Import the include() function: from django.urls import include, path 14 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 """ 16 from django.contrib import admin 17 from django.urls import path 18 from app01 import views 19 20 urlpatterns = [ 21 path('admin/', admin.site.urls), 22 path('index/', views.index), 23 path('login/', views.login), 24 25 ]
1 from django.shortcuts import render,HttpResponse,redirect 2 3 # Create your views here. 4 def index(request): 5 return HttpResponse('index') 6 # def login(request): 7 # if request.method == 'GET': 8 # return render(request,'login.html') 9 # elif request.method == 'POST': 10 # u = request.POST.get('user') 11 # p = request.POST.get('pwd') 12 # if u == 'zz'and p == '123': 13 # return redirect('/index/') 14 # else: 15 # return render(request,'login.html') 16 # else: 17 # return redirect('/index/') 18 # return render(request,'login.html') 19 20 def login(request): 21 if request.method == 'GET': 22 return render(request,'login.html') 23 elif request.method == 'POST': 24 #radio 25 # v = request.POST.get('gender') 26 # print(v) 27 28 #checkbox 29 # v = request.POST.getlist('favor') 30 # print(v) 31 32 # select 33 # v = request.POST.get('city') 34 # print(v) 35 36 #file 37 # v = request.POST.get('fff') 38 # print(v) #只会拿到文件 名�� 39 import os 40 v = request.FILES.get('fff') 41 print(v,type(v),v.name) 42 43 file_path = os.path.join('upload',v.name) 44 f = open(file_path,mode='wb') 45 for i in v.chunks(): 46 f.write(i) 47 f.close() 48 49 50 else: 51 return redirect('/index/') 52 return render(request,'login.html')
====》Django两种对应关系
/index/ -> 函数名
/index/ -> 类
CBV对应关系
class base view
1 urlpatterns = [ 2 path('admin/', admin.site.urls), 3 path('index/', views.index), 4 path('login/', views.login), 5 # path('home/', views.home), 6 path('home/', views.Home.as_view()), 7 8 ]
1 from django.shortcuts import render,HttpResponse,redirect 2 3 # Create your views here. 4 from django.views import View 5 class Home(View): 6 def dispatch(self,request,*args,**kwargs): 7 print('before') 8 result = super(Home,self).dispatch(request,*args,**kwargs) 9 print('after') 10 return result 11 def get(self,request): 12 print(request.method) 13 return render(request,'home.html') 14 def post(self,request): 15 print(request.method) 16 return render(request,'home.html')
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form action="/home/" method="post"> 9 <input type="text" name="user"/> 10 <input type="submit"/> 11 </form> 12 </body> 13 </html>
====》建议:两者都用
5、CBV和FBV用户认证装饰器
1 user_info={ 2 "zz":{'pwd':'123'} 3 } 4 def login(requset): 5 if requset.method == 'GET': 6 return render(requset,'login.html') 7 if requset.method == 'POST': 8 u = requset.POST.get('username') 9 p = requset.POST.get('pwd') 10 dic = user_info.get(u) 11 if not dic: 12 return render(requset,'login.html') 13 if dic['pwd'] == p: 14 res = redirect('/index/') 15 res.set_cookie('username',u) 16 return res 17 else: 18 return render(requset,'login.html') 19 20 def auth(func): 21 def inner(request,*args,**kwargs): 22 v = request.COOKIES.get('username') 23 if not v: 24 return redirect('/login/') 25 return func(request,*args,**kwargs) 26 return inner 27 28 @auth 29 def index(request): 30 v = request.COOKIES.get('username') 31 return render(request,'index.html',{'current_user':v})
1 from django import views 2 from django.utils.decorators import method_decorator 3 #对所有方法生效 4 @method_decorator(auth,name='dispatch') 5 class Order(views.View): 6 # 对所有的方法都生效 7 # @method_decorator(auth) 8 # def dispatch(self, request, *args, **kwargs): 9 # return super(Order,self).dispatch(request,*args,**kwargs) 10 11 # 只对单一方法做装饰(django自带) 12 # @method_decorator(auth) 13 def get(self,request): 14 v = request.COOKIES.get('username') 15 return render(request,'index.html',{'current_user':v}) 16 def post(self,request): 17 v = request.COOKIES.get('username') 18 return render(request,'index.html',{'current_user':v})
四、路由系统
1、单一路由对应
from django.urls import path
path('admin/', admin.site.urls),
path('index/', views.index),
from django.conf.urls import url
url(r'^index/', views.index),
url(r'^home/', views.Home.as_view()),
2、基于正则的路由
url(r'^detail-(\d+).html', views.detail),
url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail)
1 PS: 2 def detail(request, *args,**kwargs): 3 pass 4 实战: 5 a. 6 url(r'^detail-(\d+)-(\d+).html', views.detail), 7 def func(request, nid, uid): 8 pass 9 def func(request, *args): 10 args = (2,9) 11 def func(request, *args, **kwargs): 12 args = (2,9) 13 b. 14 url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail) 15 def func(request, nid, uid): 16 pass 17 def funct(request, **kwargs): 18 kwargs = {'nid': 1, 'uid': 3} 19 def func(request, *args, **kwargs): 20 args = (2,9)
3、设置路由映射名称
本质:对URL路由关系进行命名, ***** 以后可以根据此名称生成自己想要的URL *****
url(r'^asdfasdfasdf/', views.index, name='i1'),
url(r'^yug/(\d+)/(\d+)/', views.index, name='i2'),
url(r'^buy/(?P<pid>\d+)/(?P<nid>\d+)/', views.index, name='i3'),
设置名称之后,可以在不同的地方调用,如:
(模板中使用生成URL {% url 'h2' 2012 %}
函数中使用生成URL reverse('h2', args=(2012,)) 路径:django.urls.reverse
Model中使用获取URL 自定义get_absolute_url() 方法)
1 def func(request, *args, **kwargs): 2 from django.urls import reverse 3 url1 = reverse('i1') # asdfasdfasdf/ 4 url2 = reverse('i2', args=(1,2,)) # yug/1/2/ 5 url3 = reverse('i3', kwargs={'pid': 1, "nid": 9}) # buy/1/9/ 6 xxx.html 7 {% url "i1" %} # asdfasdfasdf/ 8 {% url "i2" 1 2 %} # yug/1/2/ 9 {% url "i3" pid=1 nid=9 %} # buy/1/9/ 10 注: 11 # 当前的URL 12 request.path_info
4、多级路由:根据app对路由规则进行分类
project/urls.py
1 from django.conf.urls import url,include 2 from django.contrib import admin 3 urlpatterns = [ 4 url(r'^cmdb/', include("app01.urls")), 5 url(r'^monitor/', include("app02.urls")), 6 ]
app01/urls.py
1 from django.conf.urls import url,include 2 from django.contrib import admin 3 from app01 import views 4 urlpatterns = [ 5 url(r'^login/', views.login), 6 ]
app02/urls.py
1 from django.conf.urls import url,include 2 from django.contrib import admin 3 from app02 import views 4 urlpatterns = [ 5 url(r'^login/', views.login), 6 ]
5、添加额外参数
url(r
'^manage/(?P<name>\w*)'
, views.manage,{
'id'
:
333
}),
6、命名空间
不使用命名空间,且两个APP某条url使用相同的name属性
from django.conf.urls import url,include urlpatterns = [ url(r'^a/', include('app01.urls')), url(r'^b/', include('app02.urls')), ]
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/', views.index, name='index') ]
from django.conf.urls import url from app02 import views urlpatterns = [ url(r'^index/', views.index, name='index') ]
from django.shortcuts import render,HttpResponse from django.urls import reverse def index(request): print('app01') return HttpResponse(reverse('index'))
from django.shortcuts import render,HttpResponse from django.urls import reverse def index(request): print('app02') return HttpResponse(reverse('index'))
显示结果:
#app01:(print:app01)/b/index/
#app02:(print:app02)/b/index/
原因:由于name没有作用域,Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,立即返回
我们在开发项目时,会经常使用name属性反解出URL,当不小心定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入了命名空间:
namespace参数:
from django.conf.urls import url,include urlpatterns = [ url(r'^a/', include('app01.urls',namespace='aaa')), url(r'^b/', include('app02.urls',namespace='bbb')), ]
from django.conf.urls import url from app01 import views app_name = 'app01' urlpatterns = [ url(r'^index/', views.index, name='index') ]
from django.conf.urls import url from app02 import views app_name = 'app02' urlpatterns = [ url(r'^index/', views.index, name='index') ]
from django.shortcuts import render,HttpResponse from django.urls import reverse def index(request): v = reverse('app01:index') print(v) print(reverse('aaa:index')) return HttpResponse(reverse('aaa:index'))
from django.shortcuts import render,HttpResponse from django.urls import reverse # Create your views here. def index(request): v = reverse('app02:index') print(v) print(reverse('bbb:index')) return HttpResponse(reverse('bbb:index'))
页面显示结果:
http://127.0.0.1:8000/a/index/:/a/index/
http://127.0.0.1:8000/b/index/:/b/index/
五、分页
1、自定义分页
from django.shortcuts import render,HttpResponse from django.urls import reverse from django.utils.safestring import mark_safe LIST = [] for i in range(1009): LIST.append(i) def user_list(request): current_page = request.GET.get('p',1) current_page = int(current_page) per_page_count = 10 pager_num = 9 start = (current_page -1) * per_page_count end = current_page * per_page_count data = LIST[start:end] all_count = len(LIST) count,y = divmod(all_count,per_page_count) if y: count += 1 page_list = [] # start_index = current_page - 5 # end_index = current_page + 6 if count < pager_num: start_index = 1 end_index = count+1 else: if current_page <=(pager_num + 1)/2: start_index = 1 end_index = pager_num + 1 else: start_index = current_page - (pager_num - 1)/2 end_index = current_page + (pager_num + 1)/2 if (current_page + (pager_num - 1)/2) > count: end_index = count + 1 start_index = count - pager_num + 1 if current_page == 1: prev = '<a class="page" href="#">上一页</a>' else: prev = '<a class="page" href="/user_list/?p=%s">上一页</a>'%(current_page-1) page_list.append(prev) for i in range(int(start_index),int(end_index)): if i == current_page: temp = '<a class="page active" href="/user_list/?p=%s">%s</a>'%(i,i) else: temp = '<a class="page" href="/user_list/?p=%s">%s</a>'%(i,i) page_list.append(temp) if current_page == count: nex = '<a class="page" href="javascript:void(0);">下一页</a>' else: nex = '<a class="page" href="/user_list/?p=%s">下一页</a>'%(current_page+1) page_list.append(nex) jump = """ <input type="text"/><a onclick='jumpTo(this,"/user_list/?p=");'>GO</a> <script> function jumpTo(ths,base){ var val = ths.previousSibling.value; location.href = base + val; } </script> """ page_list.append(jump) page_str = ''.join(page_list) page_str = mark_safe(page_str) return render(request,'user_list.html',{'li':data,'page_str':page_str})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .pagination .page{ display: inline-block; padding: 5px; margin: 5px; } .pagination .page.active{ color: white; background-color: black; } </style> </head> <body> <ul> {% for item in li %} {% include 'li.html' %} {% endfor %} </ul> <div class="pagination"> {{ page_str }} </div> </body> </html>
2、生成公共模块:
from django.shortcuts import render,HttpResponse from django.urls import reverse from django.utils.safestring import mark_safe class Page: def __init__(self,current_page,data_count,per_page_count=10,pager_num=7): self.current_page = current_page self.data_count = data_count self.per_page_count = per_page_count self.pager_num = pager_num @property def start(self): return (self.current_page -1) * self.per_page_count @property def end(self): return self.current_page * self.per_page_count @property def all_count(self): v,y = divmod(self.data_count,self.per_page_count) if y: v += 1 return v def page_str(self,base_url): page_list = [] if self.all_count < self.pager_num: start_index = 1 end_index = self.all_count+1 else: if self.current_page <=(self.pager_num + 1)/2: start_index = 1 end_index = self.pager_num + 1 else: start_index = self.current_page - (self.pager_num - 1)/2 end_index = self.current_page + (self.pager_num + 1)/2 if (self.current_page + (self.pager_num - 1)/2) > self.all_count: end_index = self.all_count + 1 start_index = self.all_count - self.pager_num + 1 if self.current_page == 1: prev = '<a class="page" href="#">上一页</a>' else: prev = '<a class="page" href="%s?p=%s">上一页</a>'%(base_url,self.current_page-1) page_list.append(prev) for i in range(int(start_index),int(end_index)): if i == self.current_page: temp = '<a class="page active" href="%s?p=%s">%s</a>'%(base_url,i,i) else: temp = '<a class="page" href="%s?p=%s">%s</a>'%(base_url,i,i) page_list.append(temp) if self.current_page == self.all_count: nex = '<a class="page" href="javascript:void(0);">下一页</a>' else: nex = '<a class="page" href="%s?p=%s">下一页</a>'%(base_url,self.current_page+1) page_list.append(nex) jump = """ <input type="text"/><a onclick='jumpTo(this,"%s?p=");'>GO</a> <script> function jumpTo(ths,base){ var val = ths.previousSibling.value; location.href = base + val; } </script> """%(base_url,) page_list.append(jump) page_str = ''.join(page_list) page_str = mark_safe(page_str) return page_str LIST = [] for i in range(1009): LIST.append(i) def user_list(request): current_page = request.GET.get('p',1) current_page = int(current_page) page_obj = Page(current_page,len(LIST),) data = LIST[page_obj.start:page_obj.end] page_str = page_obj.page_str('/user_list/') return render(request,'user_list.html',{'li':data,'page_str':page_str})
六、ORM操作
1、基本操作
a.语句对应关系:
select * from tb where id > 1
# 对应关系
models.tb.objects.filter(id__gt=1) #id大于1
models.tb.objects.filter(id=1) #id等于1
models.tb.objects.filter(id__lt=1) #id小于1
1 # 获取个数 2 # 3 # models.Tb1.objects.filter(name='seven').count() 4 5 # 大于,小于 6 # 7 # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值 8 # models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值 9 # models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值 10 # models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值 11 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 12 13 # in 14 # 15 # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 16 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in 17 18 # isnull 19 # Entry.objects.filter(pub_date__isnull=True) 20 21 # contains 22 # 23 # models.Tb1.objects.filter(name__contains="ven") 24 # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 25 # models.Tb1.objects.exclude(name__icontains="ven") 26 27 # range 28 # 29 # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and 30 31 # 其他类似 32 # 33 # startswith,istartswith, endswith, iendswith, 34 35 # order by 36 # 37 # models.Tb1.objects.filter(name='seven').order_by('id') # asc 38 # models.Tb1.objects.filter(name='seven').order_by('-id') # desc 39 40 # group by 41 # 42 # from django.db.models import Count, Min, Max, Sum 43 # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) 44 # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" 45 46 # limit 、offset 47 # 48 # models.Tb1.objects.all()[10:20] 49 50 # regex正则匹配,iregex 不区分大小写 51 # 52 # Entry.objects.get(title__regex=r'^(An?|The) +') 53 # Entry.objects.get(title__iregex=r'^(an?|the) +') 54 55 # date 56 # 57 # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) 58 # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) 59 60 # year 61 # 62 # Entry.objects.filter(pub_date__year=2005) 63 # Entry.objects.filter(pub_date__year__gte=2005) 64 65 # month 66 # 67 # Entry.objects.filter(pub_date__month=12) 68 # Entry.objects.filter(pub_date__month__gte=6) 69 70 # day 71 # 72 # Entry.objects.filter(pub_date__day=3) 73 # Entry.objects.filter(pub_date__day__gte=3) 74 75 # week_day 76 # 77 # Entry.objects.filter(pub_date__week_day=2) 78 # Entry.objects.filter(pub_date__week_day__gte=2) 79 80 # hour 81 # 82 # Event.objects.filter(timestamp__hour=23) 83 # Event.objects.filter(time__hour=5) 84 # Event.objects.filter(timestamp__hour__gte=12) 85 86 # minute 87 # 88 # Event.objects.filter(timestamp__minute=29) 89 # Event.objects.filter(time__minute=46) 90 # Event.objects.filter(timestamp__minute__gte=29) 91 92 # second 93 # 94 # Event.objects.filter(timestamp__second=31) 95 # Event.objects.filter(time__second=2) 96 # Event.objects.filter(timestamp__second__gte=31)
b.创建:
先写类:
from django.db import models # Create your models here. class UserInfo(models.Model): #id列,自增,主键 #创建用户名列,字符串类型,指定长度 username = models.CharField(max_length=32) passwoed = models.CharField(max_length=64)
注册app:
1 INSTALLED_APPS = [ 2 'django.contrib.admin', 3 'django.contrib.auth', 4 'django.contrib.contenttypes', 5 'django.contrib.sessions', 6 'django.contrib.messages', 7 'django.contrib.staticfiles', 8 'app01', 9 ]
执行命令:
python manage.py makemigrations
python manage.py migrate
c.链接其他数据库
Django默认使用的是sqlite
1 DATABASES = { 2 'default': { 3 'ENGINE': 'django.db.backends.mysql', 4 'NAME':'dbname', 5 'USER': 'root', 6 'PASSWORD': 'xxx', 7 'HOST': '', 8 'PORT': '', 9 } 10 } 11 12 # 由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替 13 # 如下设置放置的与project同名的配置的 __init__.py文件中 14 import pymysql 15 pymysql.install_as_MySQLdb()
2、其他操作
1 from app01 import models 2 def orm(request): 3 # 创建1 4 models.UserInfo.objects.create(username = 'root',passwoed = '123') 5 # 创建2 6 dic = {'username':'zz','passwoed':'123'} 7 models.UserInfo.objects.create(**dic) 8 # 创建3 9 obj = models.UserInfo(username = 'mysql',passwoed = '123') 10 obj.save() 11 12 # 查 13 result = models.UserInfo.objects.all() #查所有 14 result = models.UserInfo.objects.filter(username = 'root',passwoed = '123') #按条件查 15 # result,QuerySet =>Django => [] 16 # [obj(id,usermane,password),obj.obj] 17 for row in result: 18 print(row.id,row.username,row.passwoed) 19 print(result) 20 21 # 删 22 models.UserInfo.objects.filter(id=2).delete() 23 24 # 更新 25 models.UserInfo.objects.all().update(passwoed = 888) 26 models.UserInfo.objects.filter(id = 3).update(passwoed = 123) 27 28 return HttpResponse('orm')
1 ''' 2 # extra 3 extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) 4 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) 5 Entry.objects.extra(where=['headline=%s'], params=['Lennon']) 6 Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) 7 Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) 8 9 # F 10 from django.db.models import F 11 models.Tb1.objects.update(num=F('num')+1) 12 13 # Q 14 # 方式一: 15 Q(nid__gt=10) 16 Q(nid=8) | Q(nid__gt=10) 17 Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') 18 19 # 方式二: 20 con = Q() 21 q1 = Q() 22 q1.connector = 'OR' 23 q1.children.append(('id', 1)) 24 q1.children.append(('id', 10)) 25 q1.children.append(('id', 9)) 26 q2 = Q() 27 q2.connector = 'OR' 28 q2.children.append(('c1', 1)) 29 q2.children.append(('c1', 10)) 30 q2.children.append(('c1', 9)) 31 con.add(q1, 'AND') 32 con.add(q2, 'AND') 33 models.Tb1.objects.filter(con) 34 35 # 执行原生SQL 36 from django.db import connection, connections 37 cursor = connection.cursor() # cursor = connections['default'].cursor() 38 cursor.execute("""SELECT * from auth_user where id = %s""", [1]) 39 row = cursor.fetchone() 40 '''
示例:
基于ORM实现用户登录:
1 urlpatterns = [ 2 url(r'^login/', views.login), 3 ]
1 def login(request): 2 if request.method == 'GET': 3 return render(request,'login.html') 4 elif request.method == 'POST': 5 #数据库中执行select * from user where username = 'x' and password = 'x' 6 u = request.POST.get('user') 7 p = request.POST.get('pwd') 8 9 # obj = models.UserInfo.objects.filter(username=u,passwoed=p).first() 10 # print(obj) 11 12 # count = models.UserInfo.objects.filter(username=u,passwoed=p).count() 13 # print(count) 14 15 obj = models.UserInfo.objects.filter(username=u,passwoed=p).first() 16 if obj: 17 return redirect('/cmdb/index/') 18 else: 19 return render(request,'login.html') 20 else: 21 return redirect('/index/')
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form action="/cmdb/login/" method="post" enctype="multipart/form-data"> 9 <p> 10 <input type="text" name="user" placeholder="用户名"/> 11 </p> 12 <p> 13 <input type="password" name="pwd" placeholder="密码"/> 14 </p> 15 <input type="submit" value="提交"/> 16 </form> 17 </body> 18 </html>
用户登录成功跳转用户管理界面,基于ORM实现用户的增删改查功能:
1 urlpatterns = [ 2 url(r'^login/', views.login), 3 url(r'^index/', views.index), 4 url(r'^user_info/', views.user_info), 5 url(r'^userdetail-(?P<nid>\d+)/', views.user_detail), 6 url(r'^userdel-(?P<nid>\d+)/', views.user_del), 7 url(r'^useredit-(?P<nid>\d+)/', views.user_edit), 8 url(r'^orm/', views.orm), 9 ]
1 def user_info(request): 2 if request.method == 'GET': 3 user_list = models.UserInfo.objects.all() 4 # return render(request,'user_info.html') 5 return render(request,'user_info.html',{'user_list':user_list}) 6 elif request.method == "POST": 7 u = request.POST.get('user') 8 p = request.POST.get('pwd') 9 models.UserInfo.objects.create(username=u,passwoed=p) 10 return redirect('/cmdb/user_info') 11 12 def user_detail(request,nid): 13 obj = models.UserInfo.objects.filter(id=nid).first() 14 # models.UserInfo.objects.get(id=nid) #取单条数据如果不存在报错 15 return render(request,'user_detail.html',{'obj':obj}) 16 def user_del(request,nid): 17 models.UserInfo.objects.filter(id=nid).delete() 18 return redirect('/cmdb/user_info') 19 def user_edit(request,nid): 20 if request.method == 'GET': 21 obj = models.UserInfo.objects.filter(id=nid).first() 22 return render(request,'user_edit.html',{'obj':obj}) 23 elif request.method == 'POST': 24 nid = request.POST.get('id') 25 u = request.POST.get('username') 26 p = request.POST.get('passwoed') 27 models.UserInfo.objects.filter(id=nid).update(username=u,passwoed=p) 28 return redirect('/cmdb/user_info') 29 30 31 def detail(request,nid): 32 # return HttpResponse(nid) 33 # nid = request.GET.get('nid') 34 detail_info = USER_DICT[nid] 35 return render(request,'detail.html',{'detail_info':detail_info})
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <style> 7 body{ 8 margin: 0; 9 } 10 .menu{ 11 display: block; 12 padding: 5px; 13 } 14 </style> 15 </head> 16 <body> 17 <!-- 18 {# <ul>#} 19 {# {% for item in user_dict.values %}#} 20 {# {% for item in user_dict.keys %}#} 21 {# {% for k,item in user_dict.items %}#} 22 {# <li><a target="_blank" href="/detail/?nid={{ k }}">{{ item.name }}</a></li>#} 23 {# {% endfor %}#} 24 {# </ul>#} 25 {##} 26 {# <form action="{% url 'indexx' %}" method="POST">#} 27 {# <p><input type="text" name="user" placeholder="用户名"/></p>#} 28 {# <p><input type="text" name="email" placeholder="邮箱"/></p>#} 29 {# <input type="submit" value="提交"/>#} 30 {# </form>#} 31 {##} 32 {# <ul>#} 33 {# {% for k,item in user_dict.items %}#} 34 {# <li><a target="_blank" href="/detail-{{ k }}.html">{{ item.name }}</a></li>#} 35 {# {% endfor %}#} 36 {# </ul>#} 37 --> 38 39 40 <div style="height: 48px;background-color: black;color: white"> 41 欢迎!!!!! 42 </div> 43 <div> 44 <div style="position: absolute;top: 48px;bottom: 0;left: 0;background-color: brown;width: 200px"> 45 <a class="menu" href="/cmdb/user_info/">用户管理</a> 46 <a class="menu" href="/cmdb/user_group">用户组管理</a> 47 48 </div> 49 <div style="position: absolute;top: 48px;left: 210px;bottom:0;right: 0;overflow: auto "> 50 51 </div> 52 </div> 53 </body> 54 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <style> 7 body{ 8 margin: 0; 9 } 10 .menu{ 11 display: block; 12 padding: 5px; 13 } 14 </style> 15 </head> 16 <body> 17 18 <div style="height: 48px;background-color: black;color: white"> 19 欢迎!!!!! 20 </div> 21 <div> 22 <div style="position: absolute;top: 48px;bottom: 0;left: 0;background-color: brown;width: 200px"> 23 <a class="menu" href="/cmdb/user_info">用户管理</a> 24 <a class="menu" href="/cmdb/user_group">用户组管理</a> 25 26 </div> 27 <div style="position: absolute;top: 48px;left: 210px;bottom:0;right: 0;overflow: auto "> 28 <h3>添加用户</h3> 29 <form method="POST" action="/cmdb/user_info/"> 30 <input type="text" name="user"/> 31 <input type="text" name="pwd"/> 32 <input type="submit" value="添加"/> 33 </form> 34 <h3>用户列表</h3> 35 <ul> 36 {% for row in user_list %} 37 <li> 38 <a href="/cmdb/userdetail-{{ row.id }}/">{{ row.username }}</a> | 39 <a href="/cmdb/userdel-{{ row.id }}/">删除</a> | 40 <a href="/cmdb/useredit-{{ row.id }}/">编辑</a> | 41 </li> 42 {% endfor %} 43 </ul> 44 </div> 45 </div> 46 </body> 47 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <style> 7 body{ 8 margin: 0; 9 } 10 .menu{ 11 display: block; 12 padding: 5px; 13 } 14 </style> 15 </head> 16 <body> 17 <div style="height: 48px;background-color: black;color: white"> 18 欢迎!!!!! 19 </div> 20 <div> 21 <div style="position: absolute;top: 48px;bottom: 0;left: 0;background-color: brown;width: 200px"> 22 <a class="menu" href="/cmdb/user_info">用户管理</a> 23 <a class="menu" href="/cmdb/user_group">用户组管理</a> 24 </div> 25 <div style="position: absolute;top: 48px;left: 210px;bottom:0;right: 0;overflow: auto "> 26 <h1>用户详细信息</h1> 27 <h5>{{ obj.username }}</h5> 28 <h5>{{ obj.passwoed }}</h5> 29 </div> 30 </div> 31 </body> 32 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <style> 7 body{ 8 margin: 0; 9 } 10 .menu{ 11 display: block; 12 padding: 5px; 13 } 14 </style> 15 </head> 16 <body> 17 <div style="height: 48px;background-color: black;color: white"> 18 欢迎!!!!! 19 </div> 20 <div> 21 <div style="position: absolute;top: 48px;bottom: 0;left: 0;background-color: brown;width: 200px"> 22 <a class="menu" href="/cmdb/user_info/">用户管理</a> 23 <a class="menu" href="/cmdb/user_group">用户组管理</a> 24 </div> 25 <div style="position: absolute;top: 48px;left: 210px;bottom:0;right: 0;overflow: auto "> 26 <h1>编辑用户</h1> 27 <form method="post" action="/cmdb/useredit-{{ obj.id }}/"> 28 <input style="display: none" type="text" name="id" value="{{ obj.id }}" /> 29 <input type="text" name="username" value="{{ obj.username }}"/> 30 <input type="text" name="passwoed" value="{{ obj.passwoed }}"/> 31 <input type="submit" value="提交"> 32 </form> 33 </div> 34 </div> 35 </body> 36 </html>
3、创建数据库表
# app下的models.py
python manage.py makemigrations
python manage.py migrat
字段:
字符串类型:数字、时间、二进制、自增(primary_key=True)
1 AutoField(Field) 2 - int自增列,必须填入参数 primary_key=True 3 BigAutoField(AutoField) 4 - bigint自增列,必须填入参数 primary_key=True 5 注:当model中如果没有自增列,则自动会创建一个列名为id的列 6 from django.db import models 7 class UserInfo(models.Model): 8 # 自动创建一个列名为id的且为自增的整数列 9 username = models.CharField(max_length=32) 10 class Group(models.Model): 11 # 自定义自增列 12 nid = models.AutoField(primary_key=True) 13 name = models.CharField(max_length=32) 14 SmallIntegerField(IntegerField): 15 - 小整数 -32768 ~ 32767 16 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) 17 - 正小整数 0 ~ 32767 18 IntegerField(Field) 19 - 整数列(有符号的) -2147483648 ~ 2147483647 20 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) 21 - 正整数 0 ~ 2147483647 22 BigIntegerField(IntegerField): 23 - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 24 BooleanField(Field) 25 - 布尔值类型 26 NullBooleanField(Field): 27 - 可以为空的布尔值 28 CharField(Field) 29 - 字符类型 30 - 必须提供max_length参数, max_length表示字符长度 31 TextField(Field) 32 - 文本类型 33 EmailField(CharField): 34 - 字符串类型,Django Admin以及ModelForm中提供验证机制 35 GenericIPAddressField(Field) 36 - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 37 - 参数: 38 protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" 39 unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both" 40 URLField(CharField) 41 - 字符串类型,Django Admin以及ModelForm中提供验证 URL 42 SlugField(CharField) 43 - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) 44 CommaSeparatedIntegerField(CharField) 45 - 字符串类型,格式必须为逗号分割的数字 46 UUIDField(Field) 47 - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 48 FilePathField(Field) 49 - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 50 - 参数: 51 path, 文件夹路径 52 match=None, 正则匹配 53 recursive=False, 递归下面的文件夹 54 allow_files=True, 允许文件 55 allow_folders=False, 允许文件夹 56 FileField(Field) 57 - 字符串,路径保存在数据库,文件上传到指定目录 58 - 参数: 59 upload_to = "" 上传文件的保存路径 60 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage 61 ImageField(FileField) 62 - 字符串,路径保存在数据库,文件上传到指定目录 63 - 参数: 64 upload_to = "" 上传文件的保存路径 65 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage 66 width_field=None, 上传图片的高度保存的数据库字段名(字符串) 67 height_field=None 上传图片的宽度保存的数据库字段名(字符串) 68 DateTimeField(DateField) 69 - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 70 DateField(DateTimeCheckMixin, Field) 71 - 日期格式 YYYY-MM-DD 72 TimeField(DateTimeCheckMixin, Field) 73 - 时间格式 HH:MM[:ss[.uuuuuu]] 74 DurationField(Field) 75 - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 76 FloatField(Field) 77 - 浮点型 78 DecimalField(Field) 79 - 10进制小数 80 - 参数: 81 max_digits,小数总长度 82 decimal_places,小数位长度 83 BinaryField(Field) 84 - 二进制类型
1 null 数据库中字段是否可以为空 2 db_column 数据库中字段的列名 3 default 数据库中字段的默认值 4 primary_key 数据库中字段是否为主键 5 db_index 数据库中字段是否可以建立索引 6 unique 数据库中字段是否可以建立唯一索引 7 unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引 8 unique_for_month 数据库中字段【月】部分是否可以建立唯一索引 9 unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 10 verbose_name Admin中显示的字段名称 11 blank Admin中是否允许用户输入为空 12 editable Admin中是否可以编辑 13 help_text Admin中该字段的提示信息 14 choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 15 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) 16 error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息; 17 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date 18 如:{'null': "不能为空.", 'invalid': '格式错误'} 19 validators 自定义错误验证(列表类型),从而定制想要的验证规则 20 from django.core.validators import RegexValidator 21 from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\ 22 MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator 23 如: 24 test = models.CharField( 25 max_length=32, 26 error_messages={ 27 'c1': '优先错信息1', 28 'c2': '优先错信息2', 29 'c3': '优先错信息3', 30 }, 31 validators=[ 32 RegexValidator(regex='root_\d+', message='错误了', code='c1'), 33 RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'), 34 EmailValidator(message='又错误了', code='c3'), ] 35 )
4、一对多
创建外键(on_delete=models.CASCADE)
外键默认生成数据库时存储的为:外间字段_id
创建数据时可以根据外键的id直接创建:models.tb.object.create(name='root', user_group_id=1)
去对象中取某个单值:
userlist = models.tb.object.all()
for row in userlist:
row.id
row.user_group_id
row.user_group.caption
1 from django.db import models 2 3 # Create your models here. 4 5 class Business(models.Model): 6 caption = models.CharField(max_length=32) 7 code = models.CharField(max_length=32,null=True,default='sa') 8 9 class Host(models.Model): 10 nid = models.AutoField(primary_key=True) 11 hostname = models.CharField(max_length=32,db_index=True) 12 ip = models.GenericIPAddressField(protocol='ipv4',db_index=True) 13 port = models.IntegerField() 14 b = models.ForeignKey(to='Business',to_field='id',on_delete=models.CASCADE) 15 16 # 17 # 原因: 18 # 在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错: 19 # TypeError: __init__() missing 1 required positional argument: 'on_delete' 20 # 举例说明: 21 # user=models.OneToOneField(User) 22 # owner=models.ForeignKey(UserProfile) 23 # 需要改成: 24 # user=models.OneToOneField(User,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值 25 # owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值 26 # 参数说明: 27 # on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值 28 # CASCADE:此值设置,是级联删除。 29 # PROTECT:此值设置,是会报完整性错误。 30 # SET_NULL:此值设置,会把外键设置为null,前提是允许为null。 31 # SET_DEFAULT:此值设置,会把设置为外键的默认值。 32 # SET():此值设置,会调用外面的值,可以是一个函数。 33 # 一般情况下使用CASCADE就可以了
跨表操作
外键:
v = models.Host.objects.filter(nid__gt=0)
v[0].b.caption ----> 通过.进行跨表
一对多跨表操作的的三种方式
def host(request): v1 = models.Host.objects.filter(nid__gt=0) #QuerySet [hostboj(ip,host,另外一个对象(...)),] # for row in v1: # print(row.nid,row.hostname,row.ip,row.port,row.b_id,row.b) # print(row.b.fk.name) # return HttpResponse('host') v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption') #QuerySet [{}] print(v2) for row in v2: print(row['nid'],row['hostname'],row['b_id'],row['b__caption']) v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption') #QuerySet [()] print(v3) return render(request,'host.html',{'v1':v1,'v2':v2,'v3':v3})
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <h1>业务线列表(对象)</h1> 9 <table border="1"> 10 <thead> 11 <tr> 12 <th>主机名</th> 13 <th>IP</th> 14 <th>端口</th> 15 <th>业务线名称</th> 16 </tr> 17 </thead> 18 <tbody> 19 {% for row in v1 %} 20 <tr hid="{{ row.nid }}" bid="{{ row.b_id }}"> 21 <td>{{ row.hostname }}</td> 22 <td>{{ row.ip }}</td> 23 <td>{{ row.port }}</td> 24 <td>{{ row.b.caption }}</td> 25 </tr> 26 {% endfor %} 27 </tbody> 28 </table> 29 30 <h1>业务线列表(字典)</h1> 31 <table border="1"> 32 <thead> 33 <tr> 34 <th>主机名</th> 35 <th>业务线名称</th> 36 </tr> 37 </thead> 38 <tbody> 39 {% for row in v2 %} 40 <tr hid="{{ row.nid }}" bid="{{ row.b_id }}"> 41 <td>{{ row.hostname }}</td> 42 <td>{{ row.b__caption }}</td> 43 </tr> 44 {% endfor %} 45 </tbody> 46 </table> 47 48 <h1>业务线列表(元组)</h1> 49 <table border="1"> 50 <thead> 51 <tr> 52 <th>主机名</th> 53 <th>业务线名称</th> 54 </tr> 55 </thead> 56 <tbody> 57 {% for row in v3 %} 58 <tr hid="{{ row.0 }}" bid="{{ row.2 }}"> 59 <td>{{ row.1 }}</td> 60 <td>{{ row.3 }}</td> 61 </tr> 62 {% endfor %} 63 </tbody> 64 </table> 65 66 </body> 67 </html>
5、获取表单的三种方式
v1 = models.Business.objects.all()
# QuerySet ,内部元素都是对象
# QuerySet ,内部元素都是字典
v2 = models.Business.objects.all().values('id','caption')
# QuerySet ,内部元素都是元组
v3 = models.Business.objects.all().values_list('id','caption')
# 获取到的一个对象,如果不存在就报错
models.Business.objects.get(id=1)
对象或者None = models.Business.objects.filter(id=1).first()
def business(request): v1 = models.Business.objects.all() #QuerySet # [obj(id,caption,code),obj(id,caption,code),obj(id,caption,code)] v2 = models.Business.objects.all().values('id','caption') # select * from tb # select id,caption from ... #QuerySet # [{'id':1,'caption':'运维部'},{},{}] v3 = models.Business.objects.all().values_list('id','caption') #QuerySet # [(1,运维部),(2,开发),()] return render(request,'business.html',{'v1':v1,'v2':v2,'v3':v3})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <h1>业务线列表(对象)</h1> <ul> {% for row in v1 %} <li>{{ row.id }} - {{ row.caption }} - {{ row.code }}</li> {% endfor %} </ul> <h1>业务线列表(字典)</h1> <ul> {% for row in v2 %} <li>{{ row.id }} - {{ row.caption }}</li> {% endfor %} </ul> <h1>业务线列表(元组)</h1> <ul> {% for row in v3 %} <li>{{ row.0 }} - {{ row.1 }}</li> {% endfor %} </ul> </body> </html>
6、多对多
创建
方式一:自定义关系表
1 class Host(models.Model): 2 nid = models.AutoField(primary_key=True) 3 hostname = models.CharField(max_length=32,db_index=True) 4 ip = models.GenericIPAddressField(protocol='ipv4',db_index=True) 5 port = models.IntegerField() 6 b = models.ForeignKey(to='Business',to_field='id',on_delete=models.CASCADE) 7 class Application(models.Model): 8 name = models.CharField(max_length=32) 9 class HostToApp(models.Model): 10 hobj = models.ForeignKey(to="Host",to_field='nid',on_delete=models.CASCADE) 11 aobj = models.ForeignKey(to="Application",to_field='id',on_delete=models.CASCADE)
方式二:自动创建关系表
1 class Host(models.Model): 2 nid = models.AutoField(primary_key=True) 3 hostname = models.CharField(max_length=32,db_index=True) 4 ip = models.GenericIPAddressField(protocol='ipv4',db_index=True) 5 port = models.IntegerField() 6 b = models.ForeignKey(to='Business',to_field='id',on_delete=models.CASCADE) 7 class Application(models.Model): 8 name = models.CharField(max_length=32) 9 r = models.ManyToManyField('Host')
无法直接对第三张表进行操作
obj = Application.objects.get(id=1)
obj.name
#间接对第三张表操作
#添加数据
obj.r.add(1) -->在第三张表中插入1对应Application中的id=1
obj.r.add(1,2,3) -->增加多个关系
obj.r.add(*[1,2,3,4]) -->增加多个关系
#删除数据
obj.r.remove(1)
obj.r.remove(1,2,3)
obj.r.remove(*[1,2,3,4]) -->删除多个关系
obj.r.clear() -->删除所有Application中的id=1的所有对应关系
#修改数据
obj.r.set([3,4,5]) -->修改,执行完成此条语句数据库中只会存在(1,3)(1,4)(1,5)的对应关系,其他内容全部清除(相当于全删除在添加)
#获取数据
obj.r.all()
示例:
1 from django.db import models 2 3 # Create your models here. 4 # class Foo(models.Model): 5 # name = models.CharField(max_length=32) 6 7 class Business(models.Model): 8 caption = models.CharField(max_length=32) 9 code = models.CharField(max_length=32,null=True,default='sa') 10 # fk = models.ForeignKey('Foo') 11 12 class Host(models.Model): 13 nid = models.AutoField(primary_key=True) 14 hostname = models.CharField(max_length=32,db_index=True) 15 ip = models.GenericIPAddressField(protocol='ipv4',db_index=True) 16 port = models.IntegerField() 17 b = models.ForeignKey(to='Business',to_field='id',on_delete=models.CASCADE) 18 class Application(models.Model): 19 name = models.CharField(max_length=32) 20 r = models.ManyToManyField('Host') 21 22 # class HostToApp(models.Model): 23 # hobj = models.ForeignKey(to="Host",to_field='nid',on_delete=models.CASCADE) 24 # aobj = models.ForeignKey(to="Application",to_field='id',on_delete=models.CASCADE) 25 #
1 def app(request): 2 if request.method == 'GET': 3 app_list = models.Application.objects.all() 4 # for row in app_list: 5 # print(row.name,row.r.all()) 6 host_list = models.Host.objects.all() 7 return render(request,'app.html',{'app_list':app_list,'host_list':host_list}) 8 elif request.method == 'POST': 9 app_name = request.POST.get('app_name') 10 host_list = request.POST.getlist('host_list') 11 print(app_name,host_list) 12 obj = models.Application.objects.create(name=app_name) 13 obj.r.add(*host_list) 14 return redirect('/app') 15 def ajax_add_app(request): 16 ret = {'status':True,'error':None,'data':None} 17 print(request.POST.get('app_name')) 18 print(request.POST.getlist('host_list')) 19 # app_name = request.POST.get('app_name') 20 # host_list = request.POST.getlist('host_list') 21 # obj = models.Application.objects.create(name=app_name) 22 # obj.r.add(*host_list) 23 return HttpResponse(json.dumps(ret))
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <style> 7 .host-tag{ 8 display: inline-block; 9 padding: 3px; 10 border: 1px solid red; 11 background: pink; 12 13 } 14 .hide{ 15 display: none; 16 } 17 .shade{ 18 position: fixed; 19 top: 0; 20 right: 0; 21 left: 0; 22 bottom: 0; 23 background-color: black; 24 opacity: 0.6; 25 z-index: 100; 26 } 27 .add-mode, .edit-mode{ 28 position: fixed; 29 height: 300px; 30 width: 400px; 31 top: 100px; 32 left: 50%; 33 z-index: 101; 34 border: 1px solid red; 35 background-color: white; 36 margin-left: -200px; 37 } 38 </style> 39 </head> 40 <body> 41 <h1>应用列表</h1> 42 <div> 43 <input id="add_app" type="button" value="添加"/> 44 </div> 45 <table border="1"> 46 <thead> 47 <tr> 48 <td>应用名称</td> 49 <td>应用主机列表</td> 50 </tr> 51 </thead> 52 <tbody> 53 {% for app in app_list %} 54 <tr> 55 <td>{{ app.name }}</td> 56 <td> 57 {% for host in app.r.all %} 58 <span class="host-tag">{{ host.hostname }}</span> 59 {% endfor %} 60 </td> 61 </tr> 62 {% endfor %} 63 </tbody> 64 </table> 65 66 <div class="shade hide"></div> 67 68 <div class="add-mode hide"> 69 <form method="POST" action="/app" id="add_form"> 70 <div class="group"> 71 <input id="app_name" type="text" placeholder="应用名称" name="app_name"/> 72 </div> 73 <div class="group"> 74 <select id="host_list" name="host_list" multiple> 75 {% for op in host_list %} 76 <option value="{{ op.nid }}">{{ op.hostname }}</option> 77 {% endfor %} 78 79 </select> 80 </div> 81 <input type="submit" value="提交"/> 82 <input id="add_submit_ajax" type="button" value="Ajax提交"/> 83 </form> 84 </div> 85 <div class="edit-mode hide"> 86 <form id="edit_form" method="POST" action="/host"> 87 <input type="text" name="nid" style="display: none"/> 88 <input type="text" placeholder="主机名" name="hostname"/> 89 <input type="text" placeholder="IP" name="ip"/> 90 <input type="text" placeholder="端口" name="port"/> 91 <select name="b_id"> 92 {% for op in b_list %} 93 <option value="{{ op.id }}">{{ op.caption }}</option> 94 {% endfor %} 95 </select> 96 <a id="ajax_submit_edit" style="display: inline-block;padding: 5px;background-color: blue;color: white">确认编辑</a> 97 </form> 98 </div> 99 <script src="/static/jquery-1.12.4.js"></script> 100 <script> 101 $(function(){ 102 $('#add_app').click(function(){ 103 $('.shade,.add-mode').removeClass('hide') 104 }); 105 $('#cancle').click(function(){ 106 $('.shade,.add-mode').addClass('hide') 107 }); 108 $('#add_submit_ajax').click(function(){ 109 $.ajax({ 110 url:'/ajax_add_app', 111 {# data:{'user':123,'host_list':[1,2,3,4]},#} 112 data:$('#add_form').serialize(), 113 type:'POST', 114 dataType:'JSON', 115 traditional:true, {# 给后台发送列表需添加此参数 #} 116 success:function(obj){ 117 console.log(obj) 118 }, 119 error:function(){ 120 } 121 }) 122 }) 123 }) 124 </script> 125 </body> 126 </html>
七、ajax
1、概述
对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上
传统操作:一个简单操作需要重新加载全局数据
ajax:Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案。
异步的JavaScript:使用 【JavaScript语言】 以及 相关【浏览器提供类库】 的功能向服务端发送请求,当服务端处理完请求之后,【自动执行某个JavaScript的回调函数】,
请求和响应的整个过程是【偷偷】进行的,页面上无任何感知。
利用AJAX可以做:
1、注册时,输入用户名自动检测用户是否已经存在。
2、登陆时,提示用户名密码错误
3、删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除。
2、原生ajax
Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)
XmlHttpRequest对象的主要方法:
1 a. void open(String method,String url,Boolen async) 2 用于创建请求 3 参数: 4 method: 请求方式(字符串类型),如:POST、GET、DELETE... 5 url: 要请求的地址(字符串类型) 6 async: 是否异步(布尔类型) 7 b. void send(String body) 8 用于发送请求 9 参数: 10 body: 要发送的数据(字符串类型) 11 c. void setRequestHeader(String header,String value) 12 用于设置请求头 13 参数: 14 header: 请求头的key(字符串类型) 15 vlaue: 请求头的value(字符串类型) 16 d. String getAllResponseHeaders() 17 获取所有响应头 18 返回值: 19 响应头数据(字符串类型) 20 e. String getResponseHeader(String header) 21 获取响应头中指定header的值 22 参数: 23 header: 响应头的key(字符串类型) 24 返回值: 25 响应头中指定的header对应的值 26 f. void abort() 27 终止请求
XmlHttpRequest对象的主要属性:
1 a. Number readyState 2 状态值(整数) 3 详细: 4 0-未初始化,尚未调用open()方法; 5 1-启动,调用了open()方法,未调用send()方法; 6 2-发送,已经调用了send()方法,未接收到响应; 7 3-接收,已经接收到部分响应数据; 8 4-完成,已经接收到全部响应数据; 9 b. Function onreadystatechange 10 当readyState的值改变时自动触发执行其对应的函数(回调函数) 11 c. String responseText 12 服务器返回的数据(字符串类型) 13 d. XmlDocument responseXML 14 服务器返回的数据(Xml对象) 15 e. Number states 16 状态码(整数),如:200、404... 17 f. String statesText 18 状态文本(字符串),如:OK、NotFound...
示例:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <input type="text"/> 9 <input type="button" value="Ajax" onclick="Ajax1()"/> 10 11 <script type="text/javascript" src="/static/jquery-1.12.4.js"></script> 12 <script> 13 function getXHR(){ 14 var xhr = null; 15 if(XMLHttpRequest){ 16 xhr = new XMLHttpRequest(); 17 }else{ 18 xhr = new ActiveXObject("Microsoft.XMLHTTP"); 19 } 20 return xhr; 21 } 22 function Ajax1() { 23 {# 打开#} 24 var xhr = new XMLHttpRequest(); 25 {# xhr.open("GET",'/ajax_json/',true);#} 26 xhr.open('POST','/ajax_json/',true); 27 xhr.onreadystatechange =function () { 28 {# onreadystatechange是readyState的回调函数,判断值为多少的时候执行什么动作#} 29 if(xhr.readyState == 4){ 30 //表示接收完毕,以下打印返回值 31 var obj = JSON.parse(xhr.responseText); 32 console.log(obj); 33 } 34 }; 35 {# 发请求时候额外带个头写法,CSRF可使用#} 36 xhr.setRequestHeader('k1','v1'); 37 //django的post源码里已指定固定格式,否则后端无法收到数据 38 xhr.setRequestHeader('content-type','application/x-www-form-urlencoded; charset-UTF-8'); 39 {# 发送,send发送格式固定#} 40 xhr.send('name=root;pwd=123'); 41 } 42 </script> 43 </body> 44 </html>
1 def ajax(request): 2 return render(request,'ajax.html') 3 def ajax_json(request): 4 print(request.POST) 5 ret = {'status':True,'data':request.POST.get('username')} 6 import json 7 return HttpResponse(json.dumps(ret))
1 url(r'^ajax/$',views.ajax), 2 url(r'^ajax_json/$',views.ajax_json),
跨浏览器支持:
XmlHttpRequest:IE7+, Firefox, Chrome, Opera, etc.
ActiveXObject("Microsoft.XMLHTTP"):IE6, IE5
1 function GetXHR(){ 2 var xhr = null; 3 if(XMLHttpRequest){ 4 xhr = new XMLHttpRequest(); 5 }else{ 6 xhr = new ActiveXObject("Microsoft.XMLHTTP"); 7 } 8 return xhr; 9 }
3、jQuery Ajax
jQuery其实就是一个JavaScript的类库,其将复杂的功能做了上层封装,使得开发者可以在其基础上写更少的代码实现更多的功能。
jQuery Ajax本质 XMLHttpRequest 或 ActiveXObject
对于传统的form,可以通过表单的方式将token再次发送到服务端,而对于ajax的话,使用如下方式。
$.ajax({ # 提交到后台
url: '/host', # 提交到哪里
type: "POST", # 提交方式
# 第一种写法
data: {'k1': 123,'k2': "root"}, # 提交数据
# 第二种写法
data:$('#add_form').serialize(), #代替上一句,将form表单里所有数据统一打包发到后台
# 注意如果data字典中还包含1个字典,这个包含的字典需要转为字符串才可以发送:JSON.stringfy({'k1','v1'})
dataType:'JSON', # 这里是jquery功能,将传回来的数据进行json解析,就不需要下面的函数再次进行解析了,下面函数中的参数就为obj对象
traditional:true, # 可以将data中的列表数据传到后台,python使用get_list来接收列表数据。
success: function(data){ # 回调函数,等待接收上面提交后的返回数据
// data是服务器端返回的字符串
var obj = JSON.parse(data);
}
error:function(){ #当前台发送了一个请求到后台,后台未捕捉到发了个未知的错误,才触发这里执行
})
1 jQuery.get(...) 2 所有参数: 3 url: 待载入页面的URL地址 4 data: 待发送 Key/value 参数。 5 success: 载入成功时回调函数。 6 dataType: 返回内容格式,xml, json, script, text, html 7 jQuery.post(...) 8 所有参数: 9 url: 待载入页面的URL地址 10 data: 待发送 Key/value 参数 11 success: 载入成功时回调函数 12 dataType: 返回内容格式,xml, json, script, text, html 13 jQuery.getJSON(...) 14 所有参数: 15 url: 待载入页面的URL地址 16 data: 待发送 Key/value 参数。 17 success: 载入成功时回调函数。 18 jQuery.getScript(...) 19 所有参数: 20 url: 待载入页面的URL地址 21 data: 待发送 Key/value 参数。 22 success: 载入成功时回调函数。 23 jQuery.ajax(...) 24 部分参数: 25 url:请求地址 26 type:请求方式,GET、POST(1.9.0之后用method) 27 headers:请求头 28 data:要发送的数据 29 contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8") 30 async:是否异步 31 timeout:设置请求超时时间(毫秒) 32 beforeSend:发送请求前执行的函数(全局) 33 complete:完成之后执行的回调函数(全局) 34 success:成功之后执行的回调函数(全局) 35 error:失败之后执行的回调函数(全局) 36 accepts:通过请求头发送给服务器,告诉服务器当前客户端课接受的数据类型 37 dataType:将服务器端返回的数据转换成指定类型 38 "xml": 将服务器端返回的内容转换成xml格式 39 "text": 将服务器端返回的内容转换成普通文本格式 40 "html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。 41 "script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式 42 "json": 将服务器端返回的内容转换成相应的JavaScript对象 43 "jsonp": JSONP 格式 44 使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数 45 如果不指定,jQuery 将自动根据HTTP包MIME信息返回相应类型(an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string 46 converters: 转换器,将服务器端的内容根据指定的dataType转换类型,并传值给success回调函数
这些方法全部是调用上面的Ajax方法,不同的是只修改对应的type方法,所以说只用Ajax方法即可
建议:永远让服务器端返回一个字典
返回方式:return HttpResponse(json.dumps(字典))
不要使用render,因为返回的模板文件只做渲染,无法json转换,不支持redirect方法。
示例:
1 def test_ajax(request): 2 import json 3 ret = {'status':True,'error':None,'data':None} 4 try: 5 print(request.method,request.POST,sep='\t') 6 h = request.POST.get('hostname') 7 i = request.POST.get('ip') 8 p = request.POST.get('port') 9 b = request.POST.get('b_id') 10 if h and len(h) > 5: 11 models.Host.objects.create(hostname=h, 12 ip=i, 13 port=p, 14 b_id=b) 15 else: 16 ret['status'] = False 17 ret['error'] = '长度小于5' 18 except Exception as e: 19 ret['status'] = False 20 ret['error'] = '请求错误' 21 print(ret) 22 return HttpResponse(json.dumps(ret))
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <style> 7 .hide{ 8 display: none; 9 } 10 .shade{ 11 position: fixed; 12 top: 0; 13 right: 0; 14 left: 0; 15 bottom: 0; 16 background-color: black; 17 opacity: 0.6; 18 z-index: 100; 19 } 20 .add-mode, .edit-mode{ 21 position: fixed; 22 height: 300px; 23 width: 400px; 24 top: 100px; 25 left: 50%; 26 z-index: 101; 27 border: 1px solid red; 28 background-color: white; 29 margin-left: -200px; 30 } 31 </style> 32 </head> 33 <body> 34 <h1>主机列表(对象)</h1> 35 <div> 36 <input id="add_host" type="button" value="添加"/> 37 </div> 38 <table border="1"> 39 <thead> 40 <tr> 41 <th>序号</th> 42 <th>主机名</th> 43 <th>IP</th> 44 <th>端口</th> 45 <th>业务线名称</th> 46 <th>操作</th> 47 </tr> 48 </thead> 49 <tbody> 50 {% for row in v1 %} 51 <tr hid="{{ row.nid }}" bid="{{ row.b_id }}"> 52 <td>{{ forloop.counter }}</td> 53 <td>{{ row.hostname }}</td> 54 <td>{{ row.ip }}</td> 55 <td>{{ row.port }}</td> 56 <td>{{ row.b.caption }}</td> 57 <td> 58 <a class="edit">编辑</a> | <a class="del">删除</a> 59 </td> 60 </tr> 61 {% endfor %} 62 </tbody> 63 </table> 64 65 <h1>主机列表(字典)</h1> 66 <table border="1"> 67 <thead> 68 <tr> 69 <th>主机名</th> 70 <th>业务线名称</th> 71 </tr> 72 </thead> 73 <tbody> 74 {% for row in v2 %} 75 <tr hid="{{ row.nid }}" bid="{{ row.b_id }}"> 76 <td>{{ row.hostname }}</td> 77 <td>{{ row.b__caption }}</td> 78 </tr> 79 {% endfor %} 80 </tbody> 81 </table> 82 83 <h1>主机列表(元组)</h1> 84 <table border="1"> 85 <thead> 86 <tr> 87 <th>主机名</th> 88 <th>业务线名称</th> 89 </tr> 90 </thead> 91 <tbody> 92 {% for row in v3 %} 93 <tr hid="{{ row.0 }}" bid="{{ row.2 }}"> 94 <td>{{ row.1 }}</td> 95 <td>{{ row.3 }}</td> 96 </tr> 97 {% endfor %} 98 </tbody> 99 </table> 100 101 <div class="shade hide"></div> 102 103 <div class="add-mode hide"> 104 <form method="POST" action="/host"> 105 <div class="group"> 106 <input id="host" type="text" placeholder="主机名" name="hostname"/> 107 </div> 108 <div class="group"> 109 <input id="ip" type="text" placeholder="IP" name="ip"/> 110 </div> 111 <div class="group"> 112 <input id="port" type="text" placeholder="端口" name="port"/> 113 </div> 114 <div class="group"> 115 <select id="sel" name="b_id"> 116 {% for op in b_list %} 117 <option value="{{ op.id }}">{{ op.caption }}</option> 118 {% endfor %} 119 120 </select> 121 </div> 122 <input type="submit" value="提交"/> 123 <a id="ajax_submit" style="display: inline-block;padding: 5px;background-color: blue;color: white">悄悄提交</a> 124 <input id="cancle" type="button" value="取消"/> 125 <span id="error_msg" style="color: red"></span> 126 </form> 127 128 </div> 129 130 <script src="/static/jquery-1.12.4.js"></script> 131 <script> 132 $(function(){ 133 $('#add_host').click(function(){ 134 $('.shade,.add-mode').removeClass('hide') 135 }); 136 $('#cancle').click(function(){ 137 $('.shade,.add-mode').addClass('hide') 138 }); 139 $('#ajax_submit').click(function(){ 140 $.ajax({ 141 url:'/test_ajax', 142 type:'POST', 143 data:{'hostname':$('#host').val(),'ip':$('#ip').val(),'port':$('#port').val(),'b_id':$('#sel').val(),}, 144 success:function(data){ 145 var obj = JSON.parse(data); 146 if(obj.status){ 147 location.reload() 148 }else{ 149 $('#error_msg').text(obj.error); 150 } 151 } 152 }) 153 }); 154 }) 155 </script> 156 157 </body> 158 </html>
4、“伪”AJAX
由于HTML标签的iframe标签具有局部加载内容的特性,所以可以使用其来伪造Ajax请求
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <!-- 9 <input type="text" id="url" /> 10 <input type="button" value="发送Iframe请求" onclick="iframeRequest();" /> 11 <iframe id="ifm" src="http://www.baidu.com"></iframe> 12 --> 13 <form action="/ajax_json/" method="POST" target="ifm1"> #target=ifm使form和iframe建立管理 14 <iframe id="ifm1" name="ifm1" ></iframe> #通过iframe绑定form后进行在页面不刷新的情况下提交 15 <input type="text" name="username" /> 16 <input type="text" name="email" /> 17 <input type="submit" onclick="sumitForm();" value="Form提交"/> 18 </form> 19 <script type="text/javascript" src="/static/jquery-1.12.4.js"></script> 20 <script> 21 /* 22 function iframeRequest(){ 23 var url = $('#url').val(); 24 $('#ifm').attr('src',url); 25 } 26 */ 27 function sumitForm(){ 28 $('#ifm1').load(function(){ 29 var text = $('#ifm1').contents().find('body').text(); 30 var obj = JSON.parse(text); 31 console.log(obj) 32 }) 33 } 34 </script> 35 </body> 36 </html>
使用顺序:如果发送的是普通数据,使用顺序优先级:jquery,XMLHttpRequest,iframe
八、文件上传
1、文件上传的三种方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .upload{ display: inline-block; padding: 10px; background-color: brown; position:absolute; top: 0; bottom: 0; right: 0; left:0; z-index: 90; } .file{ width: 100px; height: 50px; opacity: 0; position:absolute; top: 0; bottom: 0; right: 0; left:0; z-index: 100; } </style> </head> <body> <div style="position:relative;width: 100px;height: 50px"> <input class="file" type="file" id="fafafa" name="afafaf"/> <a class="upload">上传</a> </div> <input type="button" value="提交XHR" onclick="xhrSubmit()"/> <input type="button" value="提交jQuery" onclick="jqSubmit()"/> <hr /> <form id="form1" action="/upload_file/" method="POST" enctype="multipart/form-data" target="ifm1"> <iframe id="ifm1" name="ifm1" style="display: none" ></iframe> <input type="file" name="fafafa" onchange="changeUpload()"/> <input type="submit" onclick="iframeForm();" value="Form提交"/> </form> <div id="preview"></div> <script type="text/javascript" src="/static/jquery-1.12.4.js"></script> <script> function changeUpload() { {# 输入框改变自动执行,提交操作#} $('#form1').submit(); iframeForm() } function jqSubmit(){ {# $('fafafa')[0]#} var file_obj = document.getElementById('fafafa').files[0]; var fd = new FormData(); fd.append('username','root'); fd.append('fafafa',file_obj); $.ajax({ url:'/upload_file/', type:'POST', data:fd, processData:false, contentType:false, success:function(arg,a1,a2) { console.log(arg); console.log(a1); console.log(a2); } }) } function xhrSubmit(){ {# $('fafafa')[0]#} var file_obj = document.getElementById('fafafa').files[0]; var fd = new FormData(); fd.append('username','root'); fd.append('fafafa',file_obj); var xhr = new XMLHttpRequest(); xhr.open('POST','/upload_file/',true); xhr.onreadystatechange = function () { if(xhr.readyState == 4){ var obj = JSON.parse(xhr.responseText); console.log(obj) } }; xhr.send(fd) } function iframeForm(){ $('#ifm1').load(function(){ var text = $('#ifm1').contents().find('body').text(); var obj = JSON.parse(text); {# 预览图片#} $('#preview').empty(); var imgTag = document.createElement('img'); imgTag.src = '/' + obj.data; $('#preview').append(imgTag) }) } </script> </body> </html>
2、后端
1 def upload(request): 2 return render(request,'upload.html') 3 def upload_file(request): 4 username = request.POST.get('username') 5 fafafa = request.FILES.get('fafafa') 6 import os 7 img_path = os.path.join('static/imgs',fafafa.name) 8 with open(img_path,'wb') as f: 9 for item in fafafa.chunks(): 10 f.write(item) 11 ret = {'code':True,'data':img_path} 12 import json 13 return HttpResponse(json.dumps(ret))
使用顺序:如果发送的是文件,使用顺序优先级:iframe,jquery(FormData),XMLHttpRequest(FormData)
九、Model操作
1、创建数据库表
1 class UserInfo(models.Model): 2 nid = models.AutoField(primary_key=True) 3 username = models.CharField(max_length=32) 4 class Meta: 5 # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名 6 db_table = "table_name" 7 # 联合索引 8 index_together = [ 9 ("pub_date", "deadline"), 10 ] 11 # 联合唯一索引 12 unique_together = (("driver", "restaurant"),) 13 # admin中显示的表名称 14 verbose_name 15 # verbose_name加s 16 verbose_name_plural
1 # app01_user 生成的表名为 tb1 2 class User(models.Model): 3 name = models.CharField(max_length=32,db_index=True) # 单列创建索引 4 email = models.CharField(max_length=32) 5 6 class Meta: # 生成的表名:tb1 7 #数据库中生成的表名称,默认app名称+下划线+类名 8 db_table='tb1' #(重要) 9 10 index_together={ ('name','email')} # 联合索引,如用户名+密码验证(重要) 11 # 联合索引根据最左前缀的模式,name最左 12 # select * from where name = 'xx' 命中索引速度快 13 # select * from where name = 'xx' and email='xx' 命中索引速度快 14 # select * from where email='xx' 无法命中索引速度慢 15 unique_together=(('name','email'),) # 联合唯一索引,组合唯一(重要) 16 verbose_name = '上课记录' 17 verbose_name_plural = '上课记录' # admin里生成的表名
一对多/一对一/多对多,其中一对一和多对多都是基于一对多衍生出来的。
一对多,重要参数:on_delete,related_name,
class UserType(models.Model): name = models.CharField(max_length=32) class User(models.Model): name = models.CharField(max_length=32) email = models.CharField(max_length=32) models.ForeignKey(to='UserTyep',to_field='id',on_delete=models.CASCADE) #on_delete有多个选项,这里这个选项表示删除一个类型,将会删除此类型对应的所有用户 # 在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错: # TypeError: __init__() missing 1 required positional argument: 'on_delete' # 举例说明: # user=models.OneToOneField(User) # owner=models.ForeignKey(UserProfile) # 需要改成: # user=models.OneToOneField(User,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值 # owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值 # 参数说明: # on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值 # CASCADE:此值设置,是级联删除。 # PROTECT:此值设置,是会报完整性错误。 # SET_NULL:此值设置,会把外键设置为null,前提是允许为null。 # SET_DEFAULT:此值设置,会把设置为外键的默认值。 # SET():此值设置,会调用外面的值,可以是一个函数。 # 一般情况下使用CASCADE就可以了
多表关系参数:
1 ForeignKey(ForeignObject) # ForeignObject(RelatedField) 2 to = '表名' # 要进行关联的表名 3 to_field=None, # 要关联的表中的字段名称 4 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 5 ''' 6 - CASCADE,删除关联数据,与之关联也删除 7 - DO_NOTHING,删除关联数据,引发错误IntegrityError,是数据库操作后库级别抛出的 8 - PROTECT,删除关联数据,引发错误ProtectedError,是受保护的,由django级别抛出的 9 - SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) 10 - SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) 11 - SET,删除关联数据, 12 a. 与之关联的值设置为指定值,设置:models.SET(值) 13 b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) 14 def func(): 15 return 10 16 class MyModel(models.Model): 17 user = models.ForeignKey( 18 to="User", 19 to_field="id" 20 on_delete=models.SET(func),) 21 ''' 22 related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() 23 related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') 24 limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: 25 ''' 26 # 如: 27 - limit_choices_to={'nid__gt': 5} 28 - limit_choices_to=lambda : {'nid__gt': 5} 29 from django.db.models import Q 30 - limit_choices_to=Q(nid__gt=10) 31 - limit_choices_to=Q(nid=8) | Q(nid__gt=10) 32 - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') 33 ''' 34 db_constraint=True # 是否在数据库中创建外键约束 35 parent_link=False # 在Admin中是否显示关联数据 36 37 38 OneToOneField(ForeignKey) 39 to = '表名' # 要进行关联的表名 40 to_field=None # 要关联的表中的字段名称 41 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 42 ###### 对于一对一 ###### 43 # 1. 一对一其实就是 一对多 + 唯一索引 44 # 2.当两个类之间有继承关系时,默认会创建一个一对一字段 45 # 如下会在A表中额外增加一个c_ptr_id列且唯一: 46 class C(models.Model): 47 nid = models.AutoField(primary_key=True) 48 part = models.CharField(max_length=12) 49 class A(C): 50 id = models.AutoField(primary_key=True) 51 code = models.CharField(max_length=1) 52 53 ManyToManyField(RelatedField) 54 to = '表名' # 要进行关联的表名 55 related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() 56 related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') 57 limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: 58 # 如: 59 ''' 60 - limit_choices_to={'nid__gt': 5} 61 - limit_choices_to=lambda : {'nid__gt': 5} 62 63 from django.db.models import Q 64 - limit_choices_to=Q(nid__gt=10) 65 - limit_choices_to=Q(nid=8) | Q(nid__gt=10) 66 - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') 67 ''' 68 symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段 69 # 做如下操作时,不同的symmetrical会有不同的可选字段 70 models.BB.objects.filter(...) 71 # 可选字段有:code, id, m1 72 class BB(models.Model): 73 code = models.CharField(max_length=12) 74 m1 = models.ManyToManyField('self',symmetrical=True) 75 # 可选字段有: bb, code, id, m1 76 class BB(models.Model): 77 code = models.CharField(max_length=12) 78 m1 = models.ManyToManyField('self',symmetrical=False) 79 through=None, # 自定义第三张表时,使用字段用于指定关系表 80 through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表 81 from django.db import models 82 class Person(models.Model): 83 name = models.CharField(max_length=50) 84 class Group(models.Model): 85 name = models.CharField(max_length=128) 86 members = models.ManyToManyField( 87 Person, 88 through='Membership', 89 through_fields=('group', 'person'), 90 ) 91 class Membership(models.Model): 92 group = models.ForeignKey(Group, on_delete=models.CASCADE) 93 person = models.ForeignKey(Person, on_delete=models.CASCADE) 94 inviter = models.ForeignKey( 95 Person, 96 on_delete=models.CASCADE, 97 related_name="membership_invites", 98 ) 99 invite_reason = models.CharField(max_length=64) 100 db_constraint=True, # 是否在数据库中创建外键约束 101 db_table=None, # 默认创建第三张表时,数据库中表的名称
2、示例:通过外键实现两个表的反向操作
1 class UserType(models.Model): 2 name = models.CharField(max_length=32) 3 class User(models.Model): 4 name = models.CharField(max_length=32) 5 pwd = models.CharField(max_length=32) 6 ut = models.ForiegnKey(to='UserTyep', to_field='id')
1 def index(request): 2 # 正向操作 3 v = models.User.objects.all() 4 for item in v: 5 print(v.user) 6 print(v.pwd) 7 print(v.ut.name) # 这个实现跨表操作 8 # 跨表提取指定的字段: ut__name为UserType的name字段 9 h = models.User.objects.all().values('user','ut__name') 10 # 反向操作 11 v = models.UserType.objects.all() 12 for item in v: 13 print(item.name) 14 print(item.user_set.all()) # 提取当前类型对应的所有用户 15 # 跨表提取指定的字段: ut__name为UserType的name字段 16 h = models.UserType.objects.all().values('name','user__pwd') 17 return HttpResponse('index')
1 # 使用参数:related_name='b' ,反向查询写法:item.b.all() 2 # 使用参数:related_query_name='a' 反向查询写法:item.a_set.all() 3 # 一对一,继承外键,并且加入了唯一约束 4 OneToOneField(ForeignKey) 5 to = '表名' # 要进行关联的表名 6 to_field=None # 要关联的表中的字段名称 7 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 8 # 多对多,关注参数:through,through_fields=None(当使用第3种方法时使用) 9 1:django创建第三张表,增删改查方法如下: 10 m2m.add/remove/set/clear/filter 11 2:自己创建第三章表(没有m2m字段),要自己实现第三章表的链表查询 12 实例: 13 class Blog(models.Model): 14 site = models.CharField(max_length=32) 15 class Tag(models.Model): 16 name = models.CharField(max_length=32) 17 class B2T(models.Model): 18 b = models.ForeignKey('Blog') 19 t = models.ForeignKey('Tag') 20 3:自定义第三张表,(有m2m字段) 21 # 通过m2m字段查操作 22 # 通过m2m字段 clear 23 实例:通过through指定第三张表,通过through_field指定第三张表的关联字段,会创建3张表,第三张表的操作需要自己手工对B2T表操作 24 class Blog(models.Model): 25 site = models.CharField(max_length=32) 26 m = models.ManyToManyField('Tag',through='B2T',through_field=['b','t1'] # 只能实现查询对Tag表 27 class Tag(models.Model): 28 name = models.CharField(max_length=32) 29 class B2T(models.Model): 30 b = models.ForeignKey('Blog') 31 t1 = models.ForeignKey('Tag') 32 t2 = models.ForeignKey('Tag')
3、QuerySet包含功能
################################################################## # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # ################################################################## def all(self) # 获取所有的数据对象 def filter(self, *args, **kwargs) # 条件查询 # 条件可以是:参数,字典,Q def exclude(self, *args, **kwargs) # 条件查询 # 条件可以是:参数,字典,Q def select_related(self, *fields) 性能相关:表之间进行join连表操作,一次性获取关联的数据。 model.tb.objects.all().select_related() model.tb.objects.all().select_related('外键字段') model.tb.objects.all().select_related('外键字段__外键字段') def prefetch_related(self, *lookups) 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。 # 获取所有用户表 # 获取用户类型表where id in (用户表中的查到的所有用户ID) models.UserInfo.objects.prefetch_related('外键字段') from django.db.models import Count, Case, When, IntegerField Article.objects.annotate( numviews=Count(Case( When(readership__what_time__lt=treshold, then=1), output_field=CharField(), )) ) students = Student.objects.all().annotate(num_excused_absences=models.Sum( models.Case( models.When(absence__type='Excused', then=1), default=0, output_field=models.IntegerField() ))) def annotate(self, *args, **kwargs) # 用于实现聚合group by查询 from django.db.models import Count, Avg, Max, Min, Sum v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')) # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1) #这里的filter相当于having # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1) # distinct去重 # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 def distinct(self, *field_names) # 用于distinct去重 models.UserInfo.objects.values('nid').distinct() # select distinct nid from userinfo 注:只有在PostgreSQL中才能使用distinct进行去重 def order_by(self, *field_names) # 用于排序 models.UserInfo.objects.all().order_by('-id','age') def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) def reverse(self): # 倒序 models.UserInfo.objects.all().order_by('-nid').reverse() # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序 def defer(self, *fields): models.UserInfo.objects.defer('username','id') 或 models.UserInfo.objects.filter(...).defer('username','id') #映射中排除某列数据 def only(self, *fields): #仅取某个表中的数据 models.UserInfo.objects.only('username','id') 或 models.UserInfo.objects.filter(...).only('username','id') def using(self, alias): 指定使用的数据库,参数为别名(setting中的设置) ################################################## # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # ################################################## def raw(self, raw_query, params=None, translations=None, using=None): # 执行原生SQL models.UserInfo.objects.raw('select * from userinfo') # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名 models.UserInfo.objects.raw('select id as nid from 其他表') # 为原生SQL设置参数 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) # 将获取的到列名转换为指定列名 name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) # 指定数据库 models.UserInfo.objects.raw('select * from userinfo', using="default") ################### 原生SQL ################### from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) row = cursor.fetchone() # fetchall()/fetchmany(..) def values(self, *fields): # 获取每行数据为字典格式 def values_list(self, *fields, **kwargs): # 获取每行数据为元祖 def dates(self, field_name, kind, order='ASC'): # 根据时间进行某一部分进行去重查找并截取指定内容 # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日) # order只能是:"ASC" "DESC" # 并获取转换后的时间 - year : 年-01-01 - month: 年-月-01 - day : 年-月-日 models.DatePlus.objects.dates('ctime','day','DESC') def datetimes(self, field_name, kind, order='ASC', tzinfo=None): # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间 # kind只能是 "year", "month", "day", "hour", "minute", "second" # order只能是:"ASC" "DESC" # tzinfo时区对象 models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC) models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) """ pip3 install pytz import pytz pytz.all_timezones pytz.timezone(‘Asia/Shanghai’) """ def none(self): # 空QuerySet对象 #################################### # METHODS THAT DO DATABASE QUERIES # #################################### def aggregate(self, *args, **kwargs): # 聚合函数,获取字典类型聚合结果 from django.db.models import Count, Avg, Max, Min, Sum result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid')) ===> {'k': 3, 'n': 4} def count(self): # 获取个数 def get(self, *args, **kwargs): # 获取单个对象 def create(self, **kwargs): # 创建对象 def bulk_create(self, objs, batch_size=None): # 批量插入 # batch_size表示一次插入的个数 objs = [ models.DDD(name='r11'), models.DDD(name='r22') ] models.DDD.objects.bulk_create(objs, 10) def get_or_create(self, defaults=None, **kwargs): # 如果存在,则获取,否则,创建 # defaults 指定创建时,其他字段的值 obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2}) def update_or_create(self, defaults=None, **kwargs): # 如果存在,则更新,否则,创建 # defaults 指定创建时或更新时的其他字段 obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1}) def first(self): # 获取第一个 def last(self): # 获取最后一个 def in_bulk(self, id_list=None): # 根据主键ID进行查找 id_list = [11,21,31] models.DDD.objects.in_bulk(id_list) def delete(self): # 删除 def update(self, **kwargs): # 更新 def exists(self): # 是否有结果
4、操作数据库表
- 基本操作
- queryset 中的方法:返回queryset类型(select_related,prefetch_related),这个2个涉及到性能
1 select_related(self, *fields) 2 select_related()帮组做跨表查询,如果做了跨表select_related()后面的参数只能加链表的字段(foreign key;many to many;one to one) 3 性能相关:表之间进行join连表操作,一次性获取关联的数据。 4 当model的这次操作的时候直接会把跨表的数据一次性全拿过来,然后在继续进行操作时就不再发起额外的sql查询了,一次性获取关联数据 5 model.tb.objects.all().select_related() 6 model.tb.objects.all().select_related('外键字段') 7 model.tb.objects.all().select_related('外键字段__外键字段') 8 不是所有的地方都适合用其来提高性能,表比较多的话性能反而降低 9 prefetch_related(self, *lookups) 10 适用于的多次查询,在第一次操作的时候执行很多次查询select_related()链表做了一次查询;而prefetch_related()链一个表会执行两次操作 11 先去把A的单表所有数据全部拿到,不涉及跨表,在其内部会隐含在执行一次查询,去B表里带着条件去查询,条件为B表id in 12 A与B为foreign key 关系,把A表中获取的全部foreign key id去B表中查询 13 两次操作完成后所有数据都会放在内存当中,Django会帮助把所有数据联合,在循环所有数据时,就自动去内存中提取,而不会再做连表查询 14 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询 在内存中做关联,而不会再做连表查询 15 # 第一次 获取所有用户表 16 # 第二次 获取用户类型表where id in (用户表中的查到的所有用户ID) 17 models.UserInfo.objects.prefetch_related('外键字段')
1 实例:sql效率不高的写法 2 def index(request): 3 # 加入用户表有10条数据 4 users = models.User.objects.all() # 这里执行一次sql请求 5 for row in users: 6 print(row.user,row.pwd,row_ut_id) 7 print(row.ut.name) # 这里外键会再次发起一次sql请求 8 print(row.ut1.name) # 这里外键会再次发起一次sql请求 9 实例:相对以上,优化写法,但是缺点是这里取到的是字典,不是对象 10 def index(request): 11 # 加入用户表有10条数据 12 users = models.User.objects.all().values('user','pwd','ut__name') 13 for row in users: 14 pass 15 实例select_related:相对第一个,只需要加1个参数select_related(),即可将关联的表一次性拿到,可以在括号内加入关联的字段,注意只能加关联的字段,满足优化 16 def index(request): 17 # 加入用户表有10条数据 18 users = models.User.objects.all().select_related('ut') 19 for row in users: 20 print(row.user,row.pwd,row_ut_id) 21 print(row.ut.name) 22 print(row.ut1.name) 23 实例prefetch_related:会进行2次sql请求,第二次会根据第一次的结果在关联表进行sql查询。如果存在多个关联参数,每个参数将会进行一次sql请求。 24 def index(request): 25 # 加入用户表有10条数据 26 users = models.User.objects.all().prefetch_related('ut') 27 # 这里会进行2次数据库sql查询,第二次查询会根据第一次的结果进行查询, 28 for row in users: 29 print(row.user,row.pwd,row_ut_id) 30 print(row.ut.name) 31 print(row.ut1.name)
5、数据验证(一般不用)
- full_clean 进行验证
经历的步骤:
- 每个字段的正则
- clean钩子
1 # 也可以在函数内自定义验证条件 2 def User(models.Mode): 3 name = models.CharField(max_length=32) 4 email = models.EmailField() 5 def clean(self): # 这里的clean是models里的钩子,用于重写操作 6 from django.core.exceptions import ValidationError 7 c = User.object.filter(name=self.name).count() 8 if c: 9 raise ValidationError(message='用户名已经存在',code='i1')
1 1.触发Model中的验证和错误提示有两种方式: 2 a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息 3 b. 使用ModelForm 4 c. 调用Model对象的 clean_fields 方法,如: 5 # models.py 6 class UserInfo(models.Model): 7 nid = models.AutoField(primary_key=True) 8 username = models.CharField(max_length=32) 9 email = models.EmailField(error_messages={'invalid': '格式错了.'}) 10 # views.py 11 def index(request): 12 obj = models.UserInfo(username='11234', email='uu') 13 try: 14 print(obj.clean_fields()) 15 except Exception as e: 16 print(e) 17 return HttpResponse('ok') 18 # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。 19 2.Admin中修改错误提示 20 # admin.py 21 from django.contrib import admin 22 from model_club import models 23 from django import forms 24 class UserInfoForm(forms.ModelForm): 25 age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'}) 26 27 class Meta: 28 model = models.UserInfo 29 # fields = ('username',) 30 fields = "__all__" 31 exclude = ['title'] 32 labels = { 'name':'Writer', } 33 help_texts = {'name':'some useful help text.',} 34 error_messages={ 'name':{'max_length':"this writer name is too long"} } 35 widgets={'name':Textarea(attrs={'cols':80,'rows':20})} 36 class UserInfoAdmin(admin.ModelAdmin): 37 form = UserInfoForm 38 admin.site.register(models.UserInfo, UserInfoAdmin)
6、其他
1 # Django原生SQL获取cursor字典 2 import pymysql 3 from django.db import connection, connections 4 connection.connect() 5 conn = connection.connection 6 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) 7 cursor.execute("""SELECT * from app01_userinfo""") 8 row = cursor.fetchone() 9 connection.close() 10 11 # 数字自增、字符串更新 12 # 数字自增 13 from django.db.models import F 14 models.UserInfo.objects.update(num=F('num') + 1) 15 # 字符串更新 16 from django.db.models.functions import Concat 17 from django.db.models import Value 18 models.UserInfo.objects.update(name=Concat('name', 'pwd')) 19 models.UserInfo.objects.update(name=Concat('name', Value('666'))) 20 21 22 # ORM函数相关 23 # ########### 基础函数 ########### 24 # 1. Concat,用于做类型转换 25 # v = models.UserInfo.objects.annotate(c=Cast('pwd', FloatField())) 26 27 # 2. Coalesce,从前向后,查询第一个不为空的值 28 # v = models.UserInfo.objects.annotate(c=Coalesce('name', 'pwd')) 29 # v = models.UserInfo.objects.annotate(c=Coalesce(Value('666'),'name', 'pwd')) 30 31 # 3. Concat,拼接 32 # models.UserInfo.objects.update(name=Concat('name', 'pwd')) 33 # models.UserInfo.objects.update(name=Concat('name', Value('666'))) 34 # models.UserInfo.objects.update(name=Concat('name', Value('666'),Value('999'))) 35 36 # 4.ConcatPair,拼接(仅两个参数) 37 # v = models.UserInfo.objects.annotate(c=ConcatPair('name', 'pwd')) 38 # v = models.UserInfo.objects.annotate(c=ConcatPair('name', Value('666'))) 39 40 # 5.Greatest,获取比较大的值;least 获取比较小的值; 41 # v = models.UserInfo.objects.annotate(c=Greatest('id', 'pwd',output_field=FloatField())) 42 43 # 6.Length,获取长度 44 # v = models.UserInfo.objects.annotate(c=Length('name')) 45 46 # 7. Lower,Upper,变大小写 47 # v = models.UserInfo.objects.annotate(c=Lower('name')) 48 # v = models.UserInfo.objects.annotate(c=Upper('name')) 49 50 # 8. Now,获取当前时间 51 # v = models.UserInfo.objects.annotate(c=Now()) 52 53 # 9. substr,子序列 54 # v = models.UserInfo.objects.annotate(c=Substr('name',1,2)) 55 56 # ########### 时间类函数 ########### 57 # 1. 时间截取,不保留其他:Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,ExtractSecond, ExtractWeekDay, ExtractYear, 58 # v = models.UserInfo.objects.annotate(c=functions.ExtractYear('ctime')) 59 # v = models.UserInfo.objects.annotate(c=functions.ExtractMonth('ctime')) 60 # v = models.UserInfo.objects.annotate(c=functions.ExtractDay('ctime')) 61 # 62 # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year')) 63 # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'month')) 64 # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year_month')) 65 """ 66 MICROSECOND 67 SECOND 68 MINUTE 69 HOUR 70 DAY 71 WEEK 72 MONTH 73 QUARTER 74 YEAR 75 SECOND_MICROSECOND 76 MINUTE_MICROSECOND 77 MINUTE_SECOND 78 HOUR_MICROSECOND 79 HOUR_SECOND 80 HOUR_MINUTE 81 DAY_MICROSECOND 82 DAY_SECOND 83 DAY_MINUTE 84 DAY_HOUR 85 YEAR_MONTH 86 """ 87 # 2. 时间截图,保留其他:Trunc, TruncDate, TruncDay,TruncHour, TruncMinute, TruncMonth, TruncSecond, TruncYear 88 # v = models.UserInfo.objects.annotate(c=functions.TruncHour('ctime')) 89 # v = models.UserInfo.objects.annotate(c=functions.TruncDate('ctime')) 90 # v = models.UserInfo.objects.annotate(c=functions.Trunc('ctime','year')) 91 92 # ORM自定义函数 93 from django.db.models.functions.base import Func 94 class CustomeFunc(Func): 95 function = 'DATE_FORMAT' 96 template = '%(function)s(%(expressions)s,%(format)s)' 97 def __init__(self, expression, **extra): 98 expressions = [expression] 99 super(CustomeFunc, self).__init__(*expressions, **extra) 100 v = models.UserInfo.objects.annotate(c=CustomeFunc('ctime',format="'%%Y-%%m'"))
十、Form组件验证
1、功能
FORM中的字段只对post上来的数据进行form验证,主要涉及:字段 和 插件
字段:对用户请求数据的验证。
插件:用于自动生成html。
- 自定义:
- 创建类
- 定义字段(验证)
- 插件(生成html)
- 初始化操作
2、示例
1 from django import forms 2 from django.forms import fields 3 # widgets 插件可以定义html的标签和样式,fields表示字段 4 from django.forms import widgets 5 class FM(forms.Form): 6 # 这里的各个字段,本身自己只做验证 7 user = fields.CharField( 8 error_messages={'required':'用户名不能为空'}, # 错误提醒 9 widget = widgets.Textarea(attrs={'class':'c1','style':'background:#e2e3e4'}), # html标签和样式 10 label = '用户名', # 标签 11 #initial='admin', # 定义初始值 12 ) 13 pwd = fields.CharField( 14 max_length=12, 15 min_length=6, 16 error_messages={'required':'密码不能为空','min_length':'密码最小长度6位','max_length':'密码最大长度为12位'}, 17 widget = widgets.PasswordInput(attrs={'style':'background:#e3e4e5'}), 18 label = '密码', 19 ) 20 email = fields.EmailField( 21 error_messages={'required':'邮箱不可以为空','invalid':'邮箱格式不正确'}, 22 label = '邮箱', 23 ) 24 f = fields.FileField( 25 label='上传文件' 26 ) 27 # 可以显示指定目录下所有的文件 28 p = fields.FilePathField( 29 path='d:/' 30 ) 31 # 下拉框 32 cite = fields.ChoiceField( 33 choices=[(0,'bj'),(1,'sh'),(2,'sz')] 34 ) 35 cite2 = fields.CharField( 36 initial=2, 37 widget=widgets.RadioSelect(choices=((0,'man'),(1,'girl'))) 38 ) 39 # 多选下拉框 40 mcite = fields.MultipleChoiceField( 41 choices=[(0,'bj'),(1,'sh'),(2,'sz')] 42 ) 43 44 def fm(request): 45 # GET将返回表单输入框 46 if request.method == 'GET': 47 # 从数据库提取数据,为字段生成默认值,注意必须为字典格式,健必须为FM的字段名 48 dic = { 49 'user':'r1', 50 'pwd':'abc', 51 'email':'test@test', 52 'cite':1, 53 'mcite':[1,2], 54 } 55 obj = FM(initial=dic) # 这里将dic作为参数传入,即可生成默认值 56 return render(request,'fm.html',{'obj':obj}) 57 if request.method == 'POST': 58 # POST获取所有提交的数据并进行校验(is_valid),cleaned_data以字典格式返回所有校验成功的数据,errors返回所有校验失败的数据, 59 # 直接将obj返回给前端,前端进行错误打印,as_json以json格式打印所有错误 60 obj = FM(request.POST) 61 ck = obj.is_valid() 62 if ck: 63 print(obj.cleaned_data) 64 # 将表单数据进行数据库添加 65 models.UserName.objects.create(**obj.cleaned_data) 66 return redirect('/fm') 67 else: 68 print(obj.errors) 69 print(obj.errors.as_json()) 70 return render(request,'fm.html',{'obj':obj})
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 {#功能:实现表单进行数据验证,并在验证未通过情况下,页面不进行刷新,只提示错误#} 9 <form action="/fm" method="post"> 10 {% csrf_token %} 11 {# 以下第一个user.label为FM表单user字段的label值,第二个obj.user为FM表单的user字段,最后的一个是FM表单的error_messages错误提醒#} 12 {% csrf_token %} 13 <p>{{ obj.user.label }}{{ obj.user }}{{ obj.errors.user.0 }}</p> 14 <p>{{ obj.pwd.label }}{{ obj.pwd }}{{obj.errors.pwd.0 }}</p> 15 <p>{{ obj.email.label }}{{ obj.email }}{{ obj.errors.email.0 }}</p> 16 <p>{{ obj.f.label }}{{ obj.f }}{{ obj.errors.f.0 }}</p> 17 <p>{#这个提交的是选中的文件的路径#} 18 {{ obj.p }} 19 </p> 20 <p>{#choice下拉框选项#} 21 {{ obj.cite }} 22 </p> 23 <p>{#单选#} 24 {{ obj.cite2 }} 25 </p> 26 <p>{{ obj.mcite }}</p> 27 其他标签有 28 {# {{ obj.as_p }}#} 29 {# {{ obj.as_ul }}#} 30 {# <table>{{ obj.as_table }}</table>#} 31 <input type="submit" value="提交" /> 32 </form> 33 </body> 34 </html>
3、form类
创建Form类时,主要涉及到 【字段】 和【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
Django内置字段:
1 Field 2 required=True, 是否允许为空 3 widget=None, HTML插件 4 label=None, 用于生成Label标签或显示内容 5 initial=None, 初始值 6 help_text='', 帮助信息(在标签旁边显示) 7 error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} 8 show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) 9 validators=[], 自定义验证规则 10 localize=False, 是否支持本地化 11 disabled=False, 是否可以编辑 12 label_suffix=None Label内容后缀 13 14 CharField(Field) 15 max_length=None, 最大长度 16 min_length=None, 最小长度 17 strip=True 是否移除用户输入空白 18 IntegerField(Field) 19 max_value=None, 最大值 20 min_value=None, 最小值 21 22 FloatField(IntegerField) 23 ... 24 25 DecimalField(IntegerField) 26 max_value=None, 最大值 27 min_value=None, 最小值 28 max_digits=None, 总长度 29 decimal_places=None, 小数位长度 30 31 BaseTemporalField(Field) 32 input_formats=None 时间格式化 33 34 DateField(BaseTemporalField) 格式:2015-09-01 35 TimeField(BaseTemporalField) 格式:11:12 36 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 37 38 DurationField(Field) 时间间隔:%d %H:%M:%S.%f 39 ... 40 41 RegexField(CharField) 42 regex, 自定制正则表达式 43 max_length=None, 最大长度 44 min_length=None, 最小长度 45 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} 46 47 EmailField(CharField) 48 ... 49 50 FileField(Field) 51 allow_empty_file=False 是否允许空文件 52 53 ImageField(FileField) 54 ... 55 注:需要PIL模块,pip3 install Pillow 56 以上两个字典使用时,需要注意两点: 57 - form表单中 enctype="multipart/form-data" 58 - view函数中 obj = MyForm(request.POST, request.FILES) 59 60 URLField(Field) 61 ... 62 63 BooleanField(Field) 64 ... 65 66 NullBooleanField(BooleanField) 67 ... 68 69 ChoiceField(Field) 70 ... 71 choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) 72 required=True, 是否必填 73 widget=None, 插件,默认select插件 74 label=None, Label内容 75 initial=None, 初始值 76 help_text='', 帮助提示 77 78 ModelChoiceField(ChoiceField) 79 ... django.forms.models.ModelChoiceField 80 queryset, # 查询数据库中的数据 81 empty_label="---------", # 默认空显示内容 82 to_field_name=None, # HTML中value的值对应的字段 83 limit_choices_to=None # ModelForm中对queryset二次筛选 84 85 ModelMultipleChoiceField(ModelChoiceField) 86 ... django.forms.models.ModelMultipleChoiceField 87 88 TypedChoiceField(ChoiceField) 89 coerce = lambda val: val 对选中的值进行一次转换 90 empty_value= '' 空值的默认值 91 92 MultipleChoiceField(ChoiceField) 93 ... 94 95 TypedMultipleChoiceField(MultipleChoiceField) 96 coerce = lambda val: val 对选中的每一个值进行一次转换 97 empty_value= '' 空值的默认值 98 99 ComboField(Field) 100 fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 101 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) 102 MultiValueField(Field) 103 PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 104 105 SplitDateTimeField(MultiValueField) 106 input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] 107 input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] 108 109 FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 110 path, 文件夹路径 111 match=None, 正则匹配 112 recursive=False, 递归下面的文件夹 113 allow_files=True, 允许文件 114 allow_folders=False, 允许文件夹 115 required=True, 116 widget=None, 117 label=None, 118 initial=None, 119 help_text='' 120 121 GenericIPAddressField 122 protocol='both', both,ipv4,ipv6支持的IP格式 123 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 124 125 SlugField(CharField) 数字,字母,下划线,减号(连字符) 126 ... 127 128 UUIDField(CharField) uuid类型 129 ... 130 ''' 131 import uuid 132 # make a UUID based on the host ID and current time 133 uuid.uuid1() # doctest: +SKIP 134 UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') 135 136 # make a UUID using an MD5 hash of a namespace UUID and a name 137 uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') 138 UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e') 139 140 # make a UUID from a string of hex digits (braces and hyphens ignored) 141 x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') 142 143 # convert a UUID to a string of hex digits in standard form 144 str(x) 145 '00010203-0405-0607-0809-0a0b0c0d0e0f' 146 147 # get the raw 16 bytes of the UUID 148 x.bytes 149 b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' 150 151 # make a UUID from a 16-byte string 152 uuid.UUID(bytes=x.bytes) 153 UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') 154 '''
Django内置插件:
1 TextInput(Input) 2 NumberInput(TextInput) 3 EmailInput(TextInput) 4 URLInput(TextInput) 5 PasswordInput(TextInput) 6 HiddenInput(TextInput) 7 Textarea(Widget) 8 DateInput(DateTimeBaseInput) 9 DateTimeInput(DateTimeBaseInput) 10 TimeInput(DateTimeBaseInput) 11 CheckboxInput 12 Select 13 NullBooleanSelect 14 SelectMultiple 15 RadioSelect 16 CheckboxSelectMultiple 17 FileInput 18 ClearableFileInput 19 MultipleHiddenInput 20 SplitDateTimeWidget 21 SplitHiddenDateTimeWidget 22 SelectDateWidget 23 24 # 单radio,值为字符串 25 user = fields.CharField( 26 initial=2, 27 widget=widgets.RadioSelect(choices=((1,'aa'),(2,'bb'),)) 28 ) 29 # 单radio,值为字符串 30 user = fields.ChoiceField( 31 choices=((1, 'aa'), (2, 'bb'),), 32 initial=2, 33 widget=widgets.RadioSelect 34 ) 35 # 单select,值为字符串 36 user = fields.CharField( 37 initial=2, 38 widget=widgets.Select(choices=((1,'aa'),(2,'bb'),)) 39 ) 40 # 单select,值为字符串 41 user = fields.ChoiceField( 42 choices=((1, 'aa'), (2, 'bb'),), 43 initial=2, 44 widget=widgets.Select 45 ) 46 # 多选select,值为列表 47 user = fields.MultipleChoiceField( 48 choices=((1,'aa'),(2,'bb'),), 49 initial=[1,], 50 widget=widgets.SelectMultiple 51 ) 52 # 单checkbox 53 user = fields.CharField( 54 widget=widgets.CheckboxInput() 55 ) 56 # 多选checkbox,值为列表 57 user = fields.MultipleChoiceField( 58 initial=[2, ], 59 choices=((1, 'aa'), (2, 'bb'),), 60 widget=widgets.CheckboxSelectMultiple 61 )
1 # 在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。 2 from django.forms import Form 3 from django.forms import widgets 4 from django.forms import fields 5 from django.core.validators import RegexValidator 6 7 class MyForm(Form): 8 user = fields.ChoiceField( 9 # choices=((1, 'aa'), (2, 'bb'),), 10 initial=2, 11 widget=widgets.Select 12 ) 13 def __init__(self, *args, **kwargs): 14 super(MyForm,self).__init__(*args, **kwargs) 15 # self.fields['user'].widget.choices = ((1, 'aa'), (2, 'bb'),) 16 # 或 17 self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption') 18 # 使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现 19 from django import forms 20 from django.forms import fields 21 from django.forms import widgets 22 from django.forms import models as form_model 23 from django.core.exceptions import ValidationError 24 from django.core.validators import RegexValidator 25 class FInfo(forms.Form): 26 authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) 27 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
每个内置插件都可以通过attr自定制属性
示例:通过表单实现单选
1 from django.forms import widgets,fields 2 from django import forms 3 from app01 import models 4 from django.forms.models import ModelChoiceField,ModelMultipleChoiceField 5 # ModelChoiceField 单选 6 # ModelMultipleChoiceField 多选 7 class UserInfoForm(forms.Form): 8 user = fields.CharField( 9 required=False, 10 widget = widgets.Textarea(attrs={'class':'c1'}) 11 ) 12 pwd = fields.CharField( 13 max_length=12, 14 widget = widgets.PasswordInput(attrs={'class':'c1'}) 15 ) 16 user_type = fields.ChoiceField( 17 # choices=[(0,'普通用户'),(1,'超级用户')], 18 choices=[], 19 widget=widgets.Select 20 ) 21 user_type2 = fields.CharField( 22 widget=widgets.Select(choices=[]) 23 ) 24 user_type3 = ModelChoiceField( 25 empty_label='请选择用户类型', 26 # 使用此方法虽然不用使用构造方法,但是表需要定制__str__方法 27 queryset=models.UserType.objects.all(), 28 to_field_name='id' # 生成select标签内option的value值 29 ) 30 def __init__(self,*args,**kwargs): 31 super(UserInfoForm,self).__init__(*args,**kwargs) 32 self.fields['user_type'].choices = models.UserType.objects.values_list('id','name') 33 self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')
1 def index2(request): 2 from app01.forms import UserInfoForm 3 obj = UserInfoForm() 4 return render(request,'indexform.html',{'obj':obj})
1 <body> 2 <p>{{ obj.user }}</p> 3 <p>{{ obj.pwd }}</p> 4 <p>{{ obj.user_type }}</p> 5 <p>{{ obj.user_type2 }}</p> 6 <p>{{ obj.user_type3 }}</p> 7 </body>
4、数据验证
经历三个阶段:
- 每一个字段(正则,字段钩子)
- clean
- _post_clean
对于错误信息:整体的错误信息放于__all__内。
示例:使用钩子进行数据验证
1 # 钩子举例,注册,登录 2 from django.core.exceptions import ValidationError 3 from django import forms 4 from app01 import models 5 from django.forms import widgets 6 from django.forms import fields 7 8 class RegisterForm(forms.Form): 9 '''字段增加验证方式,首先进行user字段的正则表达式验证,如果通过了。然后进行源码里预留的钩子clean_user验证''' 10 user = fields.CharField() 11 email = fields.EmailField() 12 13 def clean_user(self): 14 '''源码里通过钩子clean+_field,增加正则表达式验证''' 15 c = models.User.objects.filter(name=self.cleaned_data['user']).count() 16 if not c: 17 return self.cleaned_data['user'] 18 else: 19 raise ValidationError('用户名已经存在',code='xxx') 20 def clean_email(self): 21 pass 22 class LoginForm(forms.Form): 23 user = fields.CharField() 24 pwd = fields.CharField(validators=[]) # 这个参数可以自定义正则表达式验证 25 26 def clean_user(self): 27 '''第一类钩子字段验证,源码里通过钩子clean+_field,增加正则表达式验证''' 28 c = models.User.objects.filter(name=self.cleaned_data['user']).count() 29 if not c: 30 return self.cleaned_data['user'] 31 else: 32 raise ValidationError('用户名已经存在',code='xxx') 33 def clean_pwd(self): 34 pass 35 def clean(self): 36 '''第二类钩子 整体进行验证''' 37 self.cleaned_data['user'] 38 self.cleaned_data['pwd'] 39 c = models.User.objects.filter(name=self.cleaned_data['user'],pwd=self.cleaned_data['pwd']).count() 40 if c: 41 return self.cleaned_data 42 else: 43 raise ValidationError('用户名或密码错误') 44 def _post_clean(self): 45 '''第三类钩子''' 46 pass
1 def register(request): 2 from app01.forms import RegisterForm 3 obj = RegisterForm(request.POST) 4 if obj.is_valid(): 5 obj.cleaned_data 6 else: 7 obj.errors
5、初始化数据
在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。
1 1、form 2 from django.forms import Form 3 from django.forms import widgets 4 from django.forms import fields 5 from django.core.validators import RegexValidator 6 class MyForm(Form): 7 user = fields.CharField() 8 city = fields.ChoiceField( 9 choices=((1, '上海'), (2, '北京'),), 10 widget=widgets.Select 11 ) 12 2、views 13 from django.shortcuts import render, redirect 14 from .forms import MyForm 15 def index(request): 16 if request.method == "GET": 17 values = {'user': 'root', 'city': 2} 18 obj = MyForm(values) 19 return render(request, 'index.html', {'form': obj}) 20 elif request.method == "POST": 21 return redirect('http://www.google.com') 22 else: 23 return redirect('http://www.google.com') 24 3、HTML 25 <form method="POST" enctype="multipart/form-data"> 26 {% csrf_token %} 27 <p>{{ form.user }} {{ form.user.errors }}</p> 28 <p>{{ form.city }} {{ form.city.errors }}</p> 29 <input type="submit"/> 30 </form>
十一、ModelForm操作
在使用Model和Form时,都需要对字段进行定义并指定类型,通过ModelForm则可以省去From中字段的定义
1、ModelForm(耦合很强)
可以实现
1:数据库操作
2:数据验证
使用地方:1:小型项目,2:自定制jdango admin
2、功能:
1:可以生成html标签:class Meta...
2:mf = xxxModelForm(instance=ModelObj)
3:额外的标签:is_rmb=Ffields.CharField(widget=Fwidgets.CheckboxInput())
4:各种验证,is_valid() ->各种钩子
5:mf.save()
或
instance = mf.save(False)
instance.save()
mf.save_m2m()
3、字段
from django.shortcuts import render from django import forms from django.forms import fields as Ffields from app01 import models from django.forms import widgets as Fwidgets # ModelForm中的字段 class UserInfoModelForm(forms.ModelForm): # 自定制一些额外的字段 is_rmb = Ffields.CharField( widget= Fwidgets.CheckboxInput() ) class Meta: model = models.UserInfo # 对应的model fields = '__all__' # 全部字段 # fields = ['..','..'] # 指定字段名 # fields = ['username','email'] # exclude = ['username'] #除某个字段之外(排除的字段名) labels = { # 提示信息 'username':'用户名', 'email':'邮箱' } help_texts = { # 标签后的提示信息 'username':'提示信息' } widgets = { 'username':Fwidgets.Textarea(attrs={'class':'c1'}) } error_messages = { # 字段错误提醒 # '__all__':{'整体的错误信息'}, 'email':{ 'required':'邮箱不能为空','invalid':'邮箱格式不对'} } # field_classes = { # 自定义字段类,这里是将input标签的email的字段修改为url属性 # 'email':Ffields.URLField # } # localized_fields = ('ctime',) # 指定哪些字段使用东八区时间,也就是本地化 ''' 如: 数据库中 2016-12-27 04:10:57 setting中的配置 TIME_ZONE = 'Asia/Shanghai' USE_TZ = True 则显示: 2016-12-27 12:10:57 ''' # 钩子 def clean_username(self): old = self.cleaned_data['username'] return old
4、其他功能
1 2 1. 验证执行过程 3 is_valid -> full_clean -> 钩子 -> 整体错误 4 2. 字典字段验证 5 def clean_字段名(self): 6 # 可以抛出异常 7 # from django.core.exceptions import ValidationError 8 return "新值" 9 3. 用于验证 10 model_form_obj = XXOOModelForm() 11 model_form_obj.is_valid() 12 model_form_obj.errors.as_json() 13 model_form_obj.clean() 14 model_form_obj.cleaned_data 15 4. 用于创建 16 model_form_obj = XXOOModelForm(request.POST) 17 #### 页面显示,并提交 ##### 18 # 默认保存多对多 19 obj = form.save(commit=True) 20 # 不做任何操作,内部定义 save_m2m(用于保存多对多) 21 obj = form.save(commit=False) 22 obj.save() # 保存单表信息 23 obj.save_m2m() # 保存关联多对多信息 24 5. 用于更新和初始化 25 obj = model.tb.objects.get(id=1) 26 model_form_obj = XXOOModelForm(request.POST,instance=obj) 27 ... 28 PS: 单纯初始化 29 model_form_obj = XXOOModelForm(initial={...})
5、示例:用户管理
1 from django.shortcuts import render 2 from django import forms 3 from django.forms import fields as Ffields 4 from app01 import models 5 from django.forms import widgets as Fwidgets 6 7 # ModelForm中的字段 8 class UserInfoModelForm(forms.ModelForm): 9 # 自定制一些额外的字段 10 is_rmb = Ffields.CharField( 11 widget= Fwidgets.CheckboxInput() 12 ) 13 class Meta: 14 model = models.UserInfo # 对应的model 15 fields = '__all__' # 全部字段 16 # fields = ['..','..'] # 指定字段名 17 # fields = ['username','email'] 18 # exclude = ['username'] #除某个字段之外(排除的字段名) 19 labels = { # 提示信息 20 'username':'用户名', 21 'email':'邮箱' 22 } 23 help_texts = { # 标签后的提示信息 24 'username':'提示信息' 25 } 26 widgets = { 27 'username':Fwidgets.Textarea(attrs={'class':'c1'}) 28 } 29 error_messages = { # 字段错误提醒 30 # '__all__':{'整体的错误信息'}, 31 'email':{ 'required':'邮箱不能为空','invalid':'邮箱格式不对'} 32 } 33 # field_classes = { # 自定义字段类,这里是将input标签的email的字段修改为url属性 34 # 'email':Ffields.URLField 35 # } 36 # localized_fields = ('ctime',) # 指定哪些字段使用东八区时间,也就是本地化 37 ''' 38 如: 39 数据库中 40 2016-12-27 04:10:57 41 setting中的配置 42 TIME_ZONE = 'Asia/Shanghai' 43 USE_TZ = True 44 则显示: 45 2016-12-27 12:10:57 46 ''' 47 # 钩子 48 def clean_username(self): 49 old = self.cleaned_data['username'] 50 return old 51 52 class UserInfoForm(forms.Form): 53 username = Ffields.CharField(max_length=32) 54 email = Ffields.EmailField() 55 user_type = Ffields.ChoiceField( 56 choices=models.UserType.objects.values_list('id','caption') 57 ) 58 def __init__(self,*args,**kwargs): 59 super(UserInfoForm,self).__init__(*args,**kwargs) 60 self.fields['user_type'].choices=models.UserType.objects.values_list('id','caption') 61 62 def index(request): 63 if request.method == 'GET': 64 obj = UserInfoModelForm() 65 return render(request,'index.html',{'obj':obj}) 66 elif request.method == 'POST': 67 obj = UserInfoModelForm(request.POST) 68 if obj.is_valid(): 69 obj.save() 70 ''' 71 下面三句和上面的一句是等价的关系 72 instance = obj.save(False) 73 instance.save() #只会保存当前类信息不会保存manytomany 74 obj.save_m2m() 75 ''' 76 # print(obj.is_valid()) 77 # print(obj.cleaned_data) 78 # print(obj.errors.as_json()) 79 # models.UserInfo.objects.create(**obj.cleaned_data) 80 return render(request,'index.html',{'obj':obj}) 81 82 83 def user_list(request): 84 li = models.UserInfo.objects.all().select_related('user_type') 85 return render(request,'user_list.html',{'li':li}) 86 87 def user_edit(request,nid): 88 # 获取当前id对应的用户信息 89 # 显示用户已经存在的数据 90 if request.method == "GET": 91 user_obj = models.UserInfo.objects.filter(id=nid).first() 92 mf = UserInfoModelForm(instance=user_obj) 93 return render(request,'user_edit.html',{'mf':mf,'nid':nid}) 94 elif request.method == "POST": 95 user_obj = models.UserInfo.objects.filter(id=nid).first() 96 mf = UserInfoModelForm(request.POST,instance=user_obj) 97 if mf.is_valid(): 98 mf.save() 99 else: 100 print(mf.errors.as_json()) 101 return render(request,'user_edit.html',{'mf':mf,'nid':nid}) 102 103 104 105
1 from django.contrib import admin 2 from django.urls import path 3 from django.conf.urls import url 4 from app01 import views 5 6 urlpatterns = [ 7 path('admin/', admin.site.urls), 8 url(r'^index/',views.index), 9 url(r'^user_list/',views.user_list), 10 url(r'^edit-(\d+)/',views.user_edit), 11 ]
1 from django.db import models 2 3 # Create your models here. 4 5 class UserType(models.Model): 6 caption = models.CharField(max_length=32) 7 8 class UserGroup(models.Model): 9 name = models.CharField(max_length=32) 10 11 class UserInfo(models.Model): 12 username = models.CharField(max_length=32) 13 email = models.EmailField() 14 user_type = models.ForeignKey(to='UserType',to_field='id',on_delete=models.CASCADE) 15 u2g = models.ManyToManyField('UserGroup')
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <ul> 9 {% for row in li %} 10 <li>{{ row.username }} - {{ row.user_type.caption }}- <a href="/edit-{{ row.id }}/">编辑</a></li> 11 {% endfor %} 12 </ul> 13 </body> 14 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form method="POST" action="/edit-{{ nid }}/"> 9 {% csrf_token %} 10 {{ mf.as_p }} 11 <input type="submit" value="提交"/> 12 </form> 13 14 </body> 15 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form action="/index/" method="POST"> 9 {% csrf_token %} 10 {{ obj.as_p}} 11 <input type="submit" value="提交"/> 12 </form> 13 </body> 14 </html>
十二、cookie
1、获取Cookie
1 def cookie(request): 2 request.COOKIES 3 request.COOKIES['key'] #获取cookie 4 request.COOKIES.get('key') #获取cookie 5 ''' 6 request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) 7 参数: 8 default: 默认值 9 salt: 加密盐 10 max_age: 后台控制过期时间 11 '''
2、设置Cookie
(带签名的cookie:rep.set_signed_cookie)
response = HttpResponse(...) response = render(request, 'index.html') response = redirect('/index/') response.set_cookie(key,value,...) response.set_signed_cookie(key,value,salt='加密盐',...) ''' 参数: key, 键 value='', 值 max_age=None, 超时时间 expires=None, 超时时间(IE requires expires, so set it if hasn't been already.) path='/', Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问 domain=None, Cookie生效的域名 secure=False, https传输 httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖) '''
3、JavaScript和jquery也可以操作cookie
<script src
=
'/static/js/jquery.cookie.js'
><
/
script>
$.cookie(
"list_pager_num"
,
30
,{ path:
'/'
});
基于cookie实现定制显示数据条数
from django.shortcuts import render,HttpResponse,redirect from django.urls import reverse from django.utils.safestring import mark_safe class Page: def __init__(self,current_page,data_count,per_page_count=10,pager_num=7): self.current_page = current_page self.data_count = data_count self.per_page_count = per_page_count self.pager_num = pager_num @property def start(self): return (self.current_page -1) * self.per_page_count @property def end(self): return self.current_page * self.per_page_count @property def all_count(self): v,y = divmod(self.data_count,self.per_page_count) if y: v += 1 return v def page_str(self,base_url): page_list = [] if self.all_count < self.pager_num: start_index = 1 end_index = self.all_count+1 else: if self.current_page <=(self.pager_num + 1)/2: start_index = 1 end_index = self.pager_num + 1 else: start_index = self.current_page - (self.pager_num - 1)/2 end_index = self.current_page + (self.pager_num + 1)/2 if (self.current_page + (self.pager_num - 1)/2) > self.all_count: end_index = self.all_count + 1 start_index = self.all_count - self.pager_num + 1 if self.current_page == 1: prev = '<a class="page" href="#">上一页</a>' else: prev = '<a class="page" href="%s?p=%s">上一页</a>'%(base_url,self.current_page-1) page_list.append(prev) for i in range(int(start_index),int(end_index)): if i == self.current_page: temp = '<a class="page active" href="%s?p=%s">%s</a>'%(base_url,i,i) else: temp = '<a class="page" href="%s?p=%s">%s</a>'%(base_url,i,i) page_list.append(temp) if self.current_page == self.all_count: nex = '<a class="page" href="javascript:void(0);">下一页</a>' else: nex = '<a class="page" href="%s?p=%s">下一页</a>'%(base_url,self.current_page+1) page_list.append(nex) jump = """ <input type="text"/><a onclick='jumpTo(this,"%s?p=");'>GO</a> <script> function jumpTo(ths,base){ var val = ths.previousSibling.value; location.href = base + val; } </script> """%(base_url,) page_list.append(jump) page_str = ''.join(page_list) page_str = mark_safe(page_str) return page_str LIST = [] for i in range(1009): LIST.append(i) def user_list(request): current_page = request.GET.get('p',1) current_page = int(current_page) val = request.COOKIES.get('per_page_count') print(val) val = int(val) page_obj = Page(current_page,len(LIST),val) data = LIST[page_obj.start:page_obj.end] page_str = page_obj.page_str('/user_list/') return render(request,'user_list.html',{'li':data,'page_str':page_str})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .pagination .page{ display: inline-block; padding: 5px; margin: 5px; } .pagination .page.active{ color: white; background-color: black; } </style> </head> <body> <ul> {% for item in li %} {% include 'li.html' %} {% endfor %} </ul> <div> <select id="ps" onchange="changePageSize(this)"> <option value="10">10</option> <option value="30">30</option> <option value="50">50</option> <option value="100">100</option> </select> </div> <div class="pagination"> {{ page_str }} </div> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $(function(){ var v = $.cookie('per_page_count',v,{'path':'/user_list/'}); $('#ps').val(v); }); function changePageSize(ths){ var v = $(ths).val(); console.log(v); $.cookie('per_page_count',v,{'path':'/user_list/'}); location.reload(); } </script> </body> </html>
十三、session
1、session基本概念及基本操作
cookie是保存在用户浏览器端的键值对、session是保存在服务器端的键值对、session依赖于cookie
简单描述session:用户来请求登陆成功之后,服务器生成一个随机字符串给用户,并且在服务器端也保存起来,并且保存的字符串对应一个字典,放置当前用户的所有信息
服务器session操作:
1 def index(request): 2 # 获取、设置、删除Session中数据 3 request.session['k1'] #数据不存在会报错 4 request.session.get('k1',None) #不存在返回none,不报错 5 request.session['k1'] = 123 #不存在创建,存在则更新 6 request.session.setdefault('k1',123) # 存在则不设置 7 del request.session['k1'] #删除 8 9 # 所有 键、值、键值对 10 request.session.keys() 11 request.session.values() 12 request.session.items() 13 request.session.iterkeys() 14 request.session.itervalues() 15 request.session.iteritems() 16 17 # 用户session的随机字符串 18 request.session.session_key #用来获取用户端的用户随机字符串 19 # 将所有Session失效日期小于当前日期的数据删除 20 request.session.clear_expired() 21 # 检查 用户session的随机字符串 在数据库中是否 22 request.session.exists("session_key") 23 # 删除当前用户的所有Session数据 24 request.session.delete("session_key") 25 request.session.set_expiry(value) 26 * 如果value是个整数,session会在些秒数后失效。 27 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 28 * 如果value是0,用户关闭浏览器session就会失效。 29 * 如果value是None,session会依赖全局session失效策略。
在settings.py设置文件中设置默认操作:
1 SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 2 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) 3 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) 4 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) 5 SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) 6 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) 7 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) 8 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) 9 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
在settings.py设置文件中设置引擎:
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用
1 1、数据库Session 2 SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 3 2、缓存Session 4 SESSION_ENGINE = 'django.contrib.sessions.backends.cache' 5 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 6 3、文件Session 7 SESSION_ENGINE = 'django.contrib.sessions.backends.file' 8 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T 9 4、缓存+数据库Session 10 数据库用于做持久化,缓存用于提高效率 11 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' 12 5、加密cookie Session 13 SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' 14
2、用户登陆验证示例
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
简单使用session:配置好session后
执行:- python manage.py makemigrations
- python manage.py migrate
1 from django.shortcuts import render,redirect,HttpResponse 2 # Create your views here. 3 def login(request): 4 if request.method == 'GET': 5 return render(request,'login.html') 6 elif request.method == 'POST': 7 user = request.POST.get('user') 8 pwd = request.POST.get('pwd') 9 if user == 'root' and pwd == '123': 10 #生成随机字符串 11 #写到用户浏览器cookie 12 #保存到session中 13 #在随机字符串对应的字典中设置相关内容 14 15 #session中设置值 16 request.session['username'] = user 17 request.session['is_login'] = True 18 if request.POST.get('rmb',None) == '1': 19 #人为的获取设置超时时间 20 request.session.set_expiry(10) 21 return redirect('/index/') 22 else: 23 return render(request,'login.html') 24 def index(request): 25 #获取当前用户的随机字符串 26 #根据随机字符串获取对应的信息 27 28 #session中获取值 29 # if request.session['is_login']: 30 if request.session.get('is_login',None): 31 # return HttpResponse(request.session['username']) 32 return render(request,'index.html',{'username':request.session['username']}) 33 else: 34 return HttpResponse('丑拒!') 35 def logout(request): 36 request.session.clear() 37 return redirect('/login/')
1 """day22 URL Configuration 2 3 The `urlpatterns` list routes URLs to views. For more information please see: 4 https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 Examples: 6 Function views 7 1. Add an import: from my_app import views 8 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 Class-based views 10 1. Add an import: from other_app.views import Home 11 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 Including another URLconf 13 1. Import the include() function: from django.urls import include, path 14 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 """ 16 from django.contrib import admin 17 from django.urls import path 18 from django.conf.urls import url 19 from app01 import views 20 urlpatterns = [ 21 path('admin/', admin.site.urls), 22 url(r'^login/', views.login), 23 url(r'^index/', views.index), 24 url(r'^logout/', views.logout), 25 ]
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <h1>欢迎登陆,{{ username }},{{ request.session.username }}</h1> 9 <a href="/logout/">注销</a> 10 </body> 11 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form action="/login/" method="POST"> 9 <input type="text" name="user"/> 10 <input type="text" name="pwd"/> 11 <input type="checkbox" name="rmb" value="1"/> 10秒免登陆 12 <input type="submit" value="提交"/> 13 </form> 14 </body> 15 </html>
十四、跨站请求伪造
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。
而对于django中设置防跨站请求伪造功能有分为全局和局部。
1、全局:
settings.py中的:MIDDLEWARE
中间件 django.middleware.csrf.CsrfViewMiddleware
2、局部:
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
3、普通表单应用:
1 veiw中设置返回值: 2 return render_to_response('Account/Login.html',data,context_instance=RequestContext(request)) 3 或者 4 return render(request, 'xxx.html', data) 5 html中设置Token: 6 {% csrf_token %}
4、ajax应用:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form action="/login/" method="POST"> 9 {# 对于传统的form,可以通过表单的方式将token再次发送到服务端#} 10 {% csrf_token %} {# html中设置Token:#} 11 <input type="text" name="user"/> 12 <input type="text" name="pwd"/> 13 <input type="checkbox" name="rmb" value="1"/> 10秒免登陆 14 <input type="submit" value="提交"/> 15 <input id="btn" type="button" value="按钮"/> 16 </form> 17 <script src="/static/jquery-1.12.4.js"></script> 18 <script src="/static/jquery.cookie.js"></script> 19 <script> 20 {# ajax设置token#} 21 //全局设置 22 $(function(){ 23 $.ajaxSetup({ 24 beforeSend:function(xhr,setting){ 25 xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')); 26 } 27 }); 28 var csrftoken = $.cookie('csrftoken'); 29 $('#btn').click(function(){ 30 $.ajax({ 31 url:'/login/', 32 type:'POST', 33 {# headers:{'X-CSRFtoken':$.cookie('csrftoken')},#} //单独设置 34 data:{'user':'root','pwd':'123'}, 35 success:function(arg){ 36 } 37 }) 38 }) 39 }) 40 </script> 41 </body> 42 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 {% csrf_token %} 9 <input type="button" onclick="Do();" value="Do it"/> 10 <script src="/static/jquery-1.12.4.js"></script> 11 <script src="/static/jquery.cookie.js"></script> 12 <script type="text/javascript"> 13 var csrftoken = $.cookie('csrftoken'); 14 function csrfSafeMethod(method) { 15 // these HTTP methods do not require CSRF protection 16 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 17 } 18 $.ajaxSetup({ 19 beforeSend: function(xhr, settings) { 20 if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 21 xhr.setRequestHeader("X-CSRFToken", csrftoken); 22 } 23 } 24 }); 25 function Do(){ 26 $.ajax({ 27 url:"/app01/test/", 28 data:{id:1}, 29 type:'POST', 30 success:function(data){ 31 console.log(data); 32 } 33 }); 34 } 35 </script> 36 </body> 37 </html>
十五、中间件
在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件。
1、中间件中可以定义的方法:
process_request(self,request) process_view(self, request, callback, callback_args, callback_kwargs) #process_request执行完成后执行 process_response(self, request, response) process_exception(self, request, exception) #当views函数出错时执行 process_template_response(self,request,response) #如果views中的函数返回的对象中具有render方法时执行(用不到) 以上方法的返回值可以是None和HttpResonse对象,如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户。
2、自定义中间件
与mange.py在同一目录下的文件夹 Middle下的m1.py文件中:
1 from django.utils.deprecation import MiddlewareMixin 2 3 class Row1(MiddlewareMixin): 4 def process_request(self,request): 5 print('第一层进') 6 def process_response(self,request,response): 7 print('第一层出') 8 return response 9 # from django.shortcuts import HttpResponse 10 class Row2(MiddlewareMixin): 11 def process_request(self,request): 12 print('第二层进') 13 # return HttpResponse('拒绝!!!') 14 def process_response(self,request,response): 15 print('第二层出') 16 return response 17 18 class Row3(MiddlewareMixin): 19 def process_request(self,request): 20 print('第三层进') 21 def process_response(self,request,response): 22 print('第三层出') 23 return response
3、注册中间件
1 MIDDLEWARE = [ 2 'django.middleware.security.SecurityMiddleware', 3 'django.contrib.sessions.middleware.SessionMiddleware', 4 'django.middleware.common.CommonMiddleware', 5 'django.middleware.csrf.CsrfViewMiddleware', 6 'django.contrib.auth.middleware.AuthenticationMiddleware', 7 'django.contrib.messages.middleware.MessageMiddleware', 8 'django.middleware.clickjacking.XFrameOptionsMiddleware', 9 'Middle.m1.Row1', 10 'Middle.m1.Row2', 11 'Middle.m1.Row3', 12 ]
十六、缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存
缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中提供了5种缓存方式:开发调试、内存、文件、数据库、Memcache缓存(python-memcached模块、pylibmc模块)
1、配置:
1 1、开发调试方式 2 # 此为开始调试用,实际内部不做任何操作 3 # 配置: 4 CACHES = { 5 'default': { 6 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎 7 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 8 'OPTIONS':{ 9 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 10 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) 11 }, 12 'KEY_PREFIX': '', # 缓存key的前缀(默认空) 13 'VERSION': 1, # 缓存key的版本(默认1) 14 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) 15 } 16 } 17 # 自定义key 18 def default_key_func(key, key_prefix, version): 19 """ 20 Default function to generate keys. 21 Constructs the key used by all other methods. By default it prepends 22 the `key_prefix'. KEY_FUNCTION can be used to specify an alternate 23 function with custom key making behavior. 24 """ 25 return '%s:%s:%s' % (key_prefix, version, key) 26 def get_key_func(key_func): 27 """ 28 Function to decide which key function to use. 29 Defaults to ``default_key_func``. 30 """ 31 if key_func is not None: 32 if callable(key_func): 33 return key_func 34 else: 35 return import_string(key_func) 36 return default_key_func 37 2、内存方式 38 # 此缓存将内容保存至内存的变量中 39 # 配置: 40 CACHES = { 41 'default': { 42 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 43 'LOCATION': 'unique-snowflake', 44 } 45 } 46 3、文件方式 47 # 此缓存将内容保存至文件 48 # 配置: 49 50 CACHES = { 51 'default': { 52 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 53 'LOCATION': '/var/tmp/django_cache', 54 } 55 } 56 4、数据库方式 57 # 此缓存将内容保存至数据库 58 # 配置: 59 CACHES = { 60 'default': { 61 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 62 'LOCATION': 'my_cache_table', # 数据库表 63 } 64 } 65 # 注:执行创建表命令 python manage.py createcachetable 66 5、Memcache缓存方式 67 # 此缓存使用python-memcached模块连接memcache 68 CACHES = { 69 'default': { 70 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 71 'LOCATION': '127.0.0.1:11211', 72 } 73 } 74 CACHES = { 75 'default': { 76 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 77 'LOCATION': 'unix:/tmp/memcached.sock', 78 } 79 } 80 CACHES = { 81 'default': { 82 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 83 'LOCATION': [ 84 '172.19.26.240:11211', 85 '172.19.26.242:11211', 86 ] 87 } 88 } 89 # 此缓存使用pylibmc模块连接memcache 90 CACHES = { 91 'default': { 92 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 93 'LOCATION': '127.0.0.1:11211', 94 } 95 } 96 CACHES = { 97 'default': { 98 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 99 'LOCATION': '/tmp/memcached.sock', 100 } 101 } 102 CACHES = { 103 'default': { 104 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 105 'LOCATION': [ 106 '172.19.26.240:11211', 107 '172.19.26.242:11211', 108 ] 109 } 110 } 111 6、Redis缓存方式 112 CACHES = { 113 "default": { 114 "BACKEND": "django_redis.cache.RedisCache", 115 "LOCATION": "redis://127.0.0.1:6379", 116 "OPTIONS": { 117 "CLIENT_CLASS": "django_redis.client.DefaultClient", 118 "CONNECTION_POOL_KWARGS": {"max_connections": 100} 119 # "PASSWORD": "密码", 120 } 121 } 122 } 123 #视图中链接并操作 124 from django_redis import get_redis_connection 125 conn = get_redis_connection("default")
2、应用:
1 1、全站使用 2 使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 3 MIDDLEWARE = [ 4 'django.middleware.cache.UpdateCacheMiddleware', 5 # 其他中间件... 6 'django.middleware.cache.FetchFromCacheMiddleware', 7 ] 8 9 CACHE_MIDDLEWARE_ALIAS = "" 10 CACHE_MIDDLEWARE_SECONDS = "" 11 CACHE_MIDDLEWARE_KEY_PREFIX = "" 12 2、单独视图缓存 13 方式一: 14 from django.views.decorators.cache import cache_page 15 16 @cache_page(60 * 15) 17 def my_view(request): 18 ... 19 方式二: 20 from django.views.decorators.cache import cache_page 21 urlpatterns = [ 22 url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), 23 ] 24 3、局部视图使用 25 a. 引入TemplateTag 26 {% load cache %} 27 b. 使用缓存 28 {% cache 5000 缓存key %} 29 缓存内容 30 {% endcache %}
十七、信号
jango中提供了“信号调度”,用于在框架执行操作时解耦。
通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者
1、全部内置信号:
1 Model signals 2 pre_init # django的modal执行其构造方法前,自动触发 3 post_init # django的modal执行其构造方法后,自动触发 4 pre_save # django的modal对象保存前,自动触发 5 post_save # django的modal对象保存后,自动触发 6 pre_delete # django的modal对象删除前,自动触发 7 post_delete # django的modal对象删除后,自动触发 8 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 9 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 10 Management signals 11 pre_migrate # 执行migrate命令前,自动触发 12 post_migrate # 执行migrate命令后,自动触发 13 Request/response signals 14 request_started # 请求到来前,自动触发 15 request_finished # 请求结束后,自动触发 16 got_request_exception # 请求异常后,自动触发 17 Test signals 18 setting_changed # 使用test测试修改配置文件时,自动触发 19 template_rendered # 使用test测试渲染模板时,自动触发 20 Database Wrappers 21 connection_created # 创建数据库连接时,自动触发
2、信号的使用:
1 from django.core.signals import request_finished 2 from django.core.signals import request_started 3 from django.core.signals import got_request_exception 4 5 from django.db.models.signals import class_prepared 6 from django.db.models.signals import pre_init, post_init 7 from django.db.models.signals import pre_save, post_save 8 from django.db.models.signals import pre_delete, post_delete 9 from django.db.models.signals import m2m_changed 10 from django.db.models.signals import pre_migrate, post_migrate 11 12 from django.test.signals import setting_changed 13 from django.test.signals import template_rendered 14 15 from django.db.backends.signals import connection_created 16 17 def callback(sender, **kwargs): 18 print("f1_callback") 19 print(sender,kwargs) 20 f1.connect(callback) 21 # f1指上述导入的内容 22 23 from django.core.signals import request_finished 24 from django.dispatch import receiver 25 @receiver(request_finished) 26 def my_callback(sender, **kwargs): 27 print("Request finished!")
十八、动态验证码
Python生成随机验证码,需要使用PIL模块
安装:pip3 install pillow
1、基本使用:
1 1. 创建图片 2 from PIL import Image 3 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) 4 # 在图片查看器中打开 5 # img.show() 6 # 保存在本地 7 with open('code.png','wb') as f: 8 img.save(f,format='png') 9 2. 创建画笔,用于在图片上画任意内容 10 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) 11 draw = ImageDraw.Draw(img, mode='RGB') 12 3. 画点 13 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) 14 draw = ImageDraw.Draw(img, mode='RGB') 15 # 第一个参数:表示坐标 16 # 第二个参数:表示颜色 17 draw.point([100, 100], fill="red") 18 draw.point([300, 300], fill=(255, 255, 255)) 19 4. 画线 20 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) 21 draw = ImageDraw.Draw(img, mode='RGB') 22 # 第一个参数:表示起始坐标和结束坐标 23 # 第二个参数:表示颜色 24 draw.line((100,100,100,300), fill='red') 25 draw.line((100,100,300,100), fill=(255, 255, 255)) 26 5. 画圆 27 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) 28 draw = ImageDraw.Draw(img, mode='RGB') 29 # 第一个参数:表示起始坐标和结束坐标(圆要画在其中间) 30 # 第二个参数:表示开始角度 31 # 第三个参数:表示结束角度 32 # 第四个参数:表示颜色 33 draw.arc((100,100,300,300),0,90,fill="red") 34 6. 写文本 35 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) 36 draw = ImageDraw.Draw(img, mode='RGB') 37 # 第一个参数:表示起始坐标 38 # 第二个参数:表示写入内容 39 # 第三个参数:表示颜色 40 draw.text([0,0],'python',"red") 41 7. 特殊字体文字 42 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) 43 draw = ImageDraw.Draw(img, mode='RGB') 44 # 第一个参数:表示字体文件路径 45 # 第二个参数:表示字体大小 46 font = ImageFont.truetype("kumo.ttf", 28) 47 # 第一个参数:表示起始坐标 48 # 第二个参数:表示写入内容 49 # 第三个参数:表示颜色 50 # 第四个参数:表示颜色 51 draw.text([0, 0], 'python', "red", font=font)
1 import random 2 def check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28): 3 code = [] 4 img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255)) 5 draw = ImageDraw.Draw(img, mode='RGB') 6 def rndChar(): 7 """ 8 生成随机字母 9 :return: 10 """ 11 return chr(random.randint(65, 90)) 12 def rndColor(): 13 """ 14 生成随机颜色 15 :return: 16 """ 17 return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)) 18 # 写文字 19 font = ImageFont.truetype(font_file, font_size) 20 for i in range(char_length): 21 char = rndChar() 22 code.append(char) 23 h = random.randint(0, 4) 24 draw.text([i * width / char_length, h], char, font=font, fill=rndColor()) 25 # 写干扰点 26 for i in range(40): 27 draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) 28 # 写干扰圆圈 29 for i in range(40): 30 draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) 31 x = random.randint(0, width) 32 y = random.randint(0, height) 33 draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor()) 34 # 画干扰线 35 for i in range(5): 36 x1 = random.randint(0, width) 37 y1 = random.randint(0, height) 38 x2 = random.randint(0, width) 39 y2 = random.randint(0, height) 40 draw.line((x1, y1, x2, y2), fill=rndColor()) 41 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) 42 return img,''.join(code) 43 if __name__ == '__main__': 44 # 1. 直接打开 45 # img,code = check_code() 46 # img.show() 47 # 2. 写入文件 48 # img,code = check_code() 49 # with open('code.png','wb') as f: 50 # img.save(f,format='png') 51 # 3. 写入内存(Python3) 52 # from io import BytesIO 53 # stream = BytesIO() 54 # img.save(stream, 'png') 55 # stream.getvalue() 56 # 4. 写入内存(Python2) 57 # import StringIO 58 # stream = StringIO.StringIO() 59 # img.save(stream, 'png') 60 # stream.getvalue() 61 pass
2、图片验证码应用:
1 方式一: 2 # 方式一:这样的方式吧路径写死了,只能是那一张图片 3 import os 4 path = os.path.join(settings.BASE_DIR,"static","image","3.jpg") #路径拼接 5 with open(path,"rb") as f: 6 data = f.read() 7 return HttpResponse(data) 8 方式二: 9 # 方式二:每次都显示不同的图片,利用pillow模块,安装一个pillow模块 10 from PIL import Image 11 img = Image.new(mode="RGB",size=(120,40),color="green") #首先自己创建一个图片,参数size=(120,40) 代表长和高 12 f = open("validcode.png","wb")#然后把图片放在一个指定的位置 13 img.save(f,"png") #保存图片 14 f.close() 15 with open("validcode.png","rb") as f: 16 data = f.read() 17 return HttpResponse(data) 18 方式三: 19 # 方式三: 20 # 方式二也不怎么好,因为每次都要创建一个保存图片的文件,我们可以不让吧图片保存到硬盘上, 21 # 在内存中保存,完了自动清除,那么就引入了方式三:利用BytesIO模块 22 from io import BytesIO 23 from PIL import Image 24 img = Image.new(mode="RGB",size=(120,40),color="blue") 25 f = BytesIO() #内存文件句柄 26 img.save(f,"png") #保存文件 27 data = f.getvalue()#打开文件(相当于python中的f.read()) 28 return HttpResponse(data) 29 方式四: 30 # 方式四:1、添加画笔,也就是在图片上写上一些文字 31 # 2、并且字体随机,背景颜色随机 32 from io import BytesIO 33 from PIL import Image,ImageDraw,ImageFont 34 import random 35 #随机创建图片 36 img = Image.new(mode="RGB",size=(120,40),color=(random.randint(0,255),random.randint(0,255),random.randint(0,255))) 37 draw = ImageDraw.Draw(img,"RGB") 38 # 画干扰线 39 for i in range(5): 40 x1 = random.randint(0, 120) 41 y1 = random.randint(0, 40) 42 x2 = random.randint(0, 120) 43 y2 = random.randint(0, 40) 44 draw.line((x1, y1, x2, y2), fill=(random.randint(0,255),random.randint(0,255),random.randint(0,255))) 45 font = ImageFont.truetype("static/font/kumo.ttf",20) #20表示20像素 46 str_list = [] #吧每次生成的验证码保存起来 47 # 随机生成五个字符 48 for i in range(5): 49 random_num = str(random.randint(0, 9)) # 随机数字 50 random_lower = chr(random.randint(65, 90)) # 随机小写字母 51 random_upper = chr(random.randint(97, 122)) # 随机大写字母 52 random_char = random.choice([random_num, random_lower, random_upper]) 53 print(random_char,"random_char") 54 str_list.append(random_char) 55 # (5 + i * 24, 10)表示坐标,字体的位置 56 draw.text((5+i*24,10),random_char,(random.randint(0,255),random.randint(0,255),random.randint(0,255)),font=font) 57 print(str_list,"str_list") 58 f = BytesIO()#内存文件句柄 59 img.save(f,"png") #img是一个对象 60 data = f.getvalue() #读取数据并返回至HTML 61 valid_str = "".join(str_list) 62 print(valid_str,"valid_str") 63 request.session["keep_valid_code"] = valid_str #吧保存到列表的东西存放至session中 64 return HttpResponse(data)
3、示例:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>login</title> 6 <link rel="stylesheet" href="/static/commons.css" /> 7 </head> 8 <body> 9 <div> 10 <div>用户登录</div> 11 <form> 12 <div> 13 <label>用户名</label> 14 <input type="text" placeholder="请输入用户名" /> 15 </div> 16 <div> 17 <label>密码</label> 18 <input type="text" placeholder="请输入密码" /> 19 </div> 20 <div> 21 <label>验证码</label> 22 <div> 23 <input type="text" placeholder="输入验证码" name="check_code"> 24 </div> 25 <div> 26 <img src="/app04/check_code.html" onclick="changeCheckCode(this);"> 27 </div> 28 </div> 29 </form> 30 </div> 31 <script> 32 function changeCheckCode(ths) { 33 {# 可以简单实现验证码刷新#} 34 ths.src = ths.src + '?' 35 } 36 </script> 37 </body> 38 </html>
1 def check_code(request): 2 ''' 3 验证码 4 :param request: 5 :return: 6 ''' 7 from io import BytesIO 8 # 在内存创建文件 9 stream = BytesIO() 10 # 调用自定义的check_code方法生成一个图片和一个验证码字串,check_code写法请参考验证码实例 11 img,code = create_validate_code() 12 # 将图片写入到内存中以图片形式 13 img.save(stream,'PNG') 14 # 为当前请求设置session 15 request.session['CheckCode'] = code 16 # stream.getvalue()是从内存中获取图片 17 return HttpResponse(stream.getvalue()) 18 def login(request): 19 if request.method == 'POST': 20 if request.POST.get('check_code').upper() == request.session['CheckCode'].upper(): 21 print('验证码正确') 22 else: 23 print('验证码错误') 24 return render(request,'app04/login.html')
4、滑动验证码
借助插件来做
1、打开插件,找到自己需要的验证码
2、筛选有用的路径
3、把对应的视图函数也拿过来,注意还需要一个geetest.py的文件
1 #滑动验证码 2 url(r'^pc-geetest/register', pcgetcaptcha, name='pcgetcaptcha'), 3 url(r'^pc-geetest/ajax_validate', pcajax_validate, name='pcajax_validate'), 4 5 # ================ 6 from app01.geetest import GeetestLib 7 pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c" 8 pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4" 9 mobile_geetest_id = "7c25da6fe21944cfe507d2f9876775a9" 10 mobile_geetest_key = "f5883f4ee3bd4fa8caec67941de1b903" 11 # 滑动验证码 12 def pcgetcaptcha(request): 13 user_id = 'test' 14 gt = GeetestLib(pc_geetest_id, pc_geetest_key) 15 status = gt.pre_process(user_id) 16 request.session[gt.GT_STATUS_SESSION_KEY] = status 17 request.session["user_id"] = user_id 18 response_str = gt.get_response_str() 19 return HttpResponse(response_str) 20 # 滑动验证码 21 def pcajax_validate(request): 22 23 if request.method == "POST": 24 # 验证的验证码 25 ret = {"flag": False, "error_msg": None} 26 gt = GeetestLib(pc_geetest_id, pc_geetest_key) 27 challenge = request.POST.get(gt.FN_CHALLENGE, '') 28 validate = request.POST.get(gt.FN_VALIDATE, '') 29 seccode = request.POST.get(gt.FN_SECCODE, '') 30 status = request.session[gt.GT_STATUS_SESSION_KEY] 31 user_id = request.session["user_id"] 32 print("status",status) 33 if status: 34 result = gt.success_validate(challenge, validate, seccode, user_id) 35 else: 36 result = gt.failback_validate(challenge, validate, seccode) 37 if result: #如果验证验证码正确,就验证用户名是否正确 38 username = request.POST.get("username") 39 password = request.POST.get("password") 40 41 # 验证用户名和密码 42 user = auth.authenticate(username=username, password=password) 43 if user: 44 # 如果验证成功就让登录 45 ret["flag"] = True 46 auth.login(request, user) 47 else: 48 ret["error_msg"] = "用户名和密码错误" 49 else: 50 ret["error_msg"] = "验证码错误" 51 return HttpResponse(json.dumps(ret)) 52 else: 53 return render(request, "login.html")
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width"> 7 <title>Title</title> 8 <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> 9 <link rel="stylesheet" href="/static/css/login.css"> 10 <script src="/static/jquery-3.2.1.min.js"></script> 11 滑动验证码的时候导入 12 <script src="http://static.geetest.com/static/tools/gt.js"></script> 13 <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> 14 <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script> 15 16 </head> 17 <body> 18 <div class="container"> 19 <div class="row"> 20 <div class="col-md-1=10"> 21 <form class="form-horizontal" id="form_data" action="/login/" method="post"> 22 {% csrf_token %} 23 <div class="form-group"> 24 <label for="username" class="col-sm-2 control-label">用户名</label> 25 <div class="col-sm-5"> 26 <input type="text" class="form-control" id="username" placeholder="username" name="username"> 27 </div> 28 </div> 29 <div class="form-group"> 30 <label for="password" class="col-sm-2 control-label">密码</label> 31 <div class="col-sm-5"> 32 <input type="password" class="form-control" id="password" placeholder="password" name="password"> 33 </div> 34 </div> 35 <div class="form-group"> 36 <div class="row"> 37 <div class="col-md-6 col-md-offset-1"> 38 {# 文字部分#} 39 <label for="vialdCode" class="col-sm-2 control-label">验证码</label> 40 <div class="col-sm-5"> 41 <input type="text" class="form-control vialdCode_text" id="vialdCode" placeholder="验证码" name="vialdCode"> 42 </div> 43 {# 图片部分#} 44 <div class="col-md-5"> 45 <img class="vialdCode_img" src="/get_vaildCode_img/" alt="" width="200px" height="100px"> 46 {# <a href=""></a> #} 47 </div> 48 </div> 49 50 </div> 51 </div> 52 <div class="form-group"> 53 <div class="col-sm-offset-2 col-sm-10"> 54 <div class="checkbox"> 55 <label> 56 <input type="checkbox"> 下次自动登录 57 </label> 58 </div> 59 </div> 60 </div> 61 <div class="form-group"> 62 <div class="col-sm-offset-2 col-sm-10"> 63 <p> 64 <button type="button" class="btn btn-success login" id="submit">登录</button> 65 <span class="error has-error"></span></p> 66 <p> 67 <button type="button" class="btn btn-primary register">注册</button> 68 </p> 69 </div> 70 <div id="popup-captcha"></div> 71 </div> 72 </form> 73 </div> 74 </div> 75 </div> 76 {#滑动验证码#} 77 <script> 78 var handlerPopup = function (captchaObj) { 79 $("#submit").click(function () { 80 captchaObj.show(); 81 }); 82 //定时函数 83 $(".login").click(function () { 84 function foo() { 85 $(".error").html("") 86 } 87 88 // 成功的回调 89 captchaObj.onSuccess(function () { 90 var validate = captchaObj.getValidate(); 91 $.ajax({ 92 url: "/pc-geetest/ajax_validate", // 进行二次验证 93 type: "post", 94 dataType: "json", 95 headers: {"X-CSRFToken": $.cookie('csrftoken')}, 96 data: { 97 username: $('#username').val(), 98 password: $('#password').val(), 99 geetest_challenge: validate.geetest_challenge, 100 geetest_validate: validate.geetest_validate, 101 geetest_seccode: validate.geetest_seccode 102 }, 103 success: function (data) { 104 console.log(data); 105 if (data["flag"]) { 106 {# alert(location.search);#} 107 {# alert(location.search.slice(6));#} 108 {# 方式一#} 109 {# if (location.search.slice(6)) {#} 110 {# 如果用户没有登录点赞的时候,当用户后来又登录了,就直接让跳转到当前点赞的那个路径#} 111 {# location.href = location.search.slice(6)#} 112 {# }#} 113 {# else {#} 114 {# window.location.href = '/index/'#} 115 {# }#} 116 {# 方式二:#} 117 alert($.cookie("next_path")); 118 if ($.cookie("next_path")){ 119 location.href = $.cookie("next_path") 120 } 121 else{ 122 location.href = "/index/" 123 } 124 } 125 else { 126 $(".error").html(data["error_msg"]); 127 setTimeout(foo, 3000) 128 } 129 } 130 }); 131 }); 132 133 }); 134 // 将验证码加到id为captcha的元素里 135 captchaObj.appendTo("#popup-captcha"); 136 // 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html 137 }; 138 // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback) 139 $.ajax({ 140 url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存 141 type: "get", 142 dataType: "json", 143 success: function (data) { 144 // 使用initGeetest接口 145 // 参数1:配置参数 146 // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件 147 initGeetest({ 148 gt: data.gt, 149 challenge: data.challenge, 150 product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效 151 offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注 152 // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config 153 }, handlerPopup); 154 } 155 }); 156 </script>
十九、KindEditor
1、官网:http://kindeditor.net/demo.php
2、下载:http://kindeditor.net/down.php
3、文件夹说明
asp:asp示例
asp.net:asp.net示例
attached:空文件夹,放置关联文件attached
examples:HTML示例
jsp:java示例
kindeditor-all-min.js:全部JS(压缩)
kindeditor-all.js:全部JS(未压缩)
kindeditor-min.js:仅KindEditor JS(压缩)
kindeditor.js:仅KindEditor JS(未压缩)
lang:支持语言
license.txt:License
php:PHP示例
plugins:KindEditor内部使用的插件
themes:KindEditor主题
4、基本使用
1 <textarea name="content" id="content"></textarea> 2 3 <script src="/static/jquery-1.12.4.js"></script> 4 <script src="/static/plugins/kind-editor/kindeditor-all.js"></script> 5 <script> 6 $(function () { 7 initKindEditor(); 8 }); 9 10 function initKindEditor() { 11 var kind = KindEditor.create('#content', { 12 width: '100%', // 文本框宽度(可以百分比或像素) 13 height: '300px', // 文本框高度(只能像素) 14 minWidth: 200, // 最小宽度(数字) 15 minHeight: 400 // 最小高度(数字) 16 }); 17 } 18 </script>
详细参数:http://kindeditor.net/docs/option.html
5、文件操作
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <div> <h1>文章内容</h1> {{ request.POST.content|safe }} </div> <form method="POST"> <h1>请输入内容:</h1> {% csrf_token %} <div style="width: 500px; margin: 0 auto;"> <textarea name="content" id="content"></textarea> </div> <input type="submit" value="提交"/> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/plugins/kind-editor/kindeditor-all.js"></script> <script> $(function () { initKindEditor(); }); function initKindEditor() { var a = 'kind'; var kind = KindEditor.create('#content', { width: '100%', // 文本框宽度(可以百分比或像素) height: '300px', // 文本框高度(只能像素) minWidth: 200, // 最小宽度(数字) minHeight: 400, // 最小高度(数字) uploadJson: '/kind/upload_img/', extraFileUploadParams: { 'csrfmiddlewaretoken': '{{ csrf_token }}' }, fileManagerJson: '/kind/file_manager/', allowPreviewEmoticons: true, allowImageUpload: true }); } </script> </body> </html> HTML
1 import os 2 import json 3 import time 4 5 from django.shortcuts import render 6 from django.shortcuts import HttpResponse 7 8 9 def index(request): 10 """ 11 首页 12 :param request: 13 :return: 14 """ 15 return render(request, 'index.html') 16 17 18 def upload_img(request): 19 """ 20 文件上传 21 :param request: 22 :return: 23 """ 24 dic = { 25 'error': 0, 26 'url': '/static/imgs/20130809170025.png', 27 'message': '错误了...' 28 } 29 30 return HttpResponse(json.dumps(dic)) 31 32 33 def file_manager(request): 34 """ 35 文件管理 36 :param request: 37 :return: 38 """ 39 dic = {} 40 root_path = '/Users/wupeiqi/PycharmProjects/editors/static/' 41 static_root_path = '/static/' 42 request_path = request.GET.get('path') 43 if request_path: 44 abs_current_dir_path = os.path.join(root_path, request_path) 45 move_up_dir_path = os.path.dirname(request_path.rstrip('/')) 46 dic['moveup_dir_path'] = move_up_dir_path + '/' if move_up_dir_path else move_up_dir_path 47 48 else: 49 abs_current_dir_path = root_path 50 dic['moveup_dir_path'] = '' 51 52 dic['current_dir_path'] = request_path 53 dic['current_url'] = os.path.join(static_root_path, request_path) 54 55 file_list = [] 56 for item in os.listdir(abs_current_dir_path): 57 abs_item_path = os.path.join(abs_current_dir_path, item) 58 a, exts = os.path.splitext(item) 59 is_dir = os.path.isdir(abs_item_path) 60 if is_dir: 61 temp = { 62 'is_dir': True, 63 'has_file': True, 64 'filesize': 0, 65 'dir_path': '', 66 'is_photo': False, 67 'filetype': '', 68 'filename': item, 69 'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path))) 70 } 71 else: 72 temp = { 73 'is_dir': False, 74 'has_file': False, 75 'filesize': os.stat(abs_item_path).st_size, 76 'dir_path': '', 77 'is_photo': True if exts.lower() in ['.jpg', '.png', '.jpeg'] else False, 78 'filetype': exts.lower().strip('.'), 79 'filename': item, 80 'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path))) 81 } 82 83 file_list.append(temp) 84 dic['file_list'] = file_list 85 return HttpResponse(json.dumps(dic)) 86 87 View
6、XSS过滤特殊标签
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 from bs4 import BeautifulSoup 4 5 6 class XSSFilter(object): 7 __instance = None 8 9 def __init__(self): 10 # XSS白名单 11 self.valid_tags = { 12 "font": ['color', 'size', 'face', 'style'], 13 'b': [], 14 'div': [], 15 "span": [], 16 "table": [ 17 'border', 'cellspacing', 'cellpadding' 18 ], 19 'th': [ 20 'colspan', 'rowspan' 21 ], 22 'td': [ 23 'colspan', 'rowspan' 24 ], 25 "a": ['href', 'target', 'name'], 26 "img": ['src', 'alt', 'title'], 27 'p': [ 28 'align' 29 ], 30 "pre": ['class'], 31 "hr": ['class'], 32 'strong': [] 33 } 34 35 @classmethod 36 def instance(cls): 37 if not cls.__instance: 38 obj = cls() 39 cls.__instance = obj 40 return cls.__instance 41 42 def process(self, content): 43 soup = BeautifulSoup(content, 'lxml') 44 # 遍历所有HTML标签 45 for tag in soup.find_all(recursive=True): 46 # 判断标签名是否在白名单中 47 if tag.name not in self.valid_tags: 48 tag.hidden = True 49 if tag.name not in ['html', 'body']: 50 tag.hidden = True 51 tag.clear() 52 continue 53 # 当前标签的所有属性白名单 54 attr_rules = self.valid_tags[tag.name] 55 keys = list(tag.attrs.keys()) 56 for key in keys: 57 if key not in attr_rules: 58 del tag[key] 59 60 return soup.renderContents() 61 62 63 if __name__ == '__main__': 64 html = """<p class="title"> 65 <b>The Dormouse's story</b> 66 </p> 67 <p class="story"> 68 <div name='root'> 69 Once upon a time there were three little sisters; and their names were 70 <a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a> 71 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and 72 <a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>; 73 and they lived at the bottom of a well. 74 <script>alert(123)</script> 75 </div> 76 </p> 77 <p class="story">...</p>""" 78 79 v = XSSFilter.instance().process(html) 80 print(v)
1 #基于__new__实现单例模式示例 2 from bs4 import BeautifulSoup 3 4 5 class XSSFilter(object): 6 __instance = None 7 8 def __init__(self): 9 # XSS白名单 10 self.valid_tags = { 11 "font": ['color', 'size', 'face', 'style'], 12 'b': [], 13 'div': [], 14 "span": [], 15 "table": [ 16 'border', 'cellspacing', 'cellpadding' 17 ], 18 'th': [ 19 'colspan', 'rowspan' 20 ], 21 'td': [ 22 'colspan', 'rowspan' 23 ], 24 "a": ['href', 'target', 'name'], 25 "img": ['src', 'alt', 'title'], 26 'p': [ 27 'align' 28 ], 29 "pre": ['class'], 30 "hr": ['class'], 31 'strong': [] 32 } 33 34 def __new__(cls, *args, **kwargs): 35 """ 36 单例模式 37 :param cls: 38 :param args: 39 :param kwargs: 40 :return: 41 """ 42 if not cls.__instance: 43 obj = object.__new__(cls, *args, **kwargs) 44 cls.__instance = obj 45 return cls.__instance 46 47 def process(self, content): 48 soup = BeautifulSoup(content, 'lxml') 49 # 遍历所有HTML标签 50 for tag in soup.find_all(recursive=True): 51 # 判断标签名是否在白名单中 52 if tag.name not in self.valid_tags: 53 tag.hidden = True 54 if tag.name not in ['html', 'body']: 55 tag.hidden = True 56 tag.clear() 57 continue 58 # 当前标签的所有属性白名单 59 attr_rules = self.valid_tags[tag.name] 60 keys = list(tag.attrs.keys()) 61 for key in keys: 62 if key not in attr_rules: 63 del tag[key] 64 65 return soup.renderContents() 66 67 68 if __name__ == '__main__': 69 html = """<p class="title"> 70 <b>The Dormouse's story</b> 71 </p> 72 <p class="story"> 73 <div name='root'> 74 Once upon a time there were three little sisters; and their names were 75 <a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a> 76 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and 77 <a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>; 78 and they lived at the bottom of a well. 79 <script>alert(123)</script> 80 </div> 81 </p> 82 <p class="story">...</p>""" 83 84 obj = XSSFilter() 85 v = obj.process(html) 86 print(v)
二十、组合搜索
1、基本方法实现
1 from django.shortcuts import render 2 from app01 import models 3 # Create your views here. 4 def article(request,*args,**kwargs): 5 # request.path_info #获取当前url 6 # from django.urls import reverse 7 # 8 # reverse('article',kwargs=kwargs) 9 # print(kwargs) 10 article_type_list = models.ArticleType.objects.all() 11 category_list = models.Category.objects.all() 12 condition ={} 13 for k,v in kwargs.items(): 14 kwargs[k] = int(v) 15 if v == '0': 16 pass 17 else: 18 condition[k] = v 19 result = models.Article.objects.filter(**condition) 20 return render( 21 request, 22 'article.html', 23 { 24 'result':result, 25 'article_type_list':article_type_list, 26 'category_list':category_list, 27 'arg_dict':kwargs 28 })
1 from django.contrib import admin 2 from django.urls import path 3 from django.conf.urls import url 4 from app01 import views 5 6 urlpatterns = [ 7 path('admin/', admin.site.urls), 8 url(r'^article-(?P<article_type_id>\d+)-(?P<category_id>\d+).html',views.article,name='article'), 9 ]
1 from django.db import models 2 3 # Create your models here. 4 class Category(models.Model): 5 caption = models.CharField(max_length=16) 6 class ArticleType(models.Model): 7 caption = models.CharField(max_length=16) 8 class Article(models.Model): 9 title = models.CharField(max_length=32) 10 content = models.CharField(max_length=255) 11 category = models.ForeignKey(Category,on_delete=models.CASCADE) 12 article_type = models.ForeignKey(ArticleType,on_delete=models.CASCADE) 13 # type_choice = ( 14 # (0,'Python'), 15 # (1,'OpenStack'), 16 # (2,'Linux'), 17 # ) 18 # article_type_id = models.IntegerField(choices=type_choice)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <style> 7 .condition a{ 8 display:inline-block; 9 padding: 3px 5px; 10 border:1px solid #dddddd; 11 margin: 5px 5px; 12 } 13 .condition a.active{ 14 background-color:brown; 15 } 16 </style> 17 </head> 18 <body> 19 <h1>过滤条件</h1> 20 <div class="condition"> 21 <div> 22 {% if arg_dict.article_type_id == 0 %} 23 <a class="active" href="/article-0-{{ arg_dict.category_id }}.html">全部</a> 24 {% else %} 25 <a href="/article-0-{{ arg_dict.category_id }}.html">全部</a> 26 {% endif %} 27 {% for row in article_type_list %} 28 {% if row.id == arg_dict.article_type_id%} 29 <a class="active" href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a> 30 {% else %} 31 <a href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a> 32 {% endif %} 33 {% endfor %} 34 </div> 35 <div> 36 {% if arg_dict.category_id == 0 %} 37 <a class="active" href="/article-{{ arg_dict.article_type_id }}-0.html">全部</a> 38 {% else %} 39 <a href="/article-{{ arg_dict.article_type_id }}-0.html">全部</a> 40 {% endif %} 41 {% for row in category_list %} 42 {% if row.id == arg_dict.category_id%} 43 <a class="active" href="/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html">{{ row.caption }}</a> 44 {% else %} 45 <a href="/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html">{{ row.caption }}</a> 46 {% endif %} 47 {% endfor %} 48 </div> 49 </div> 50 <h1>查询结果</h1> 51 <ul> 52 {% for row in result %} 53 <li>{{ row.id }}-{{ row.title }}</li> 54 {% endfor %} 55 </ul> 56 </body> 57 </html>
2、simple_tag版
1 {% load filter %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>Title</title> 7 <style> 8 .condition a{ 9 display:inline-block; 10 padding: 3px 5px; 11 border:1px solid #dddddd; 12 margin: 5px 5px; 13 } 14 .condition a.active{ 15 background-color:brown; 16 } 17 </style> 18 </head> 19 <body> 20 <h1>过滤条件</h1> 21 <div class="condition"> 22 <div> 23 {% filter_all arg_dict 'article_type_id'%} 24 {% filter_article_type article_type_list arg_dict 'article_type_id' %} 25 </div> 26 <div> 27 {% filter_all arg_dict 'category_id' %} 28 {% filter_article_type category_list arg_dict 'category_id' %} 29 </div> 30 </div> 31 <h1>查询结果</h1> 32 <ul> 33 {% for row in result %} 34 <li>{{ row.id }}-{{ row.title }}</li> 35 {% endfor %} 36 </ul> 37 </body> 38 </html>
1 from django import template 2 from django.utils.safestring import mark_safe 3 register = template.Library() 4 @register.simple_tag 5 def filter_all(arg_dict,k): 6 ''' 7 {% if arg_dict.article_type_id == 0 %} 8 <a class="active" href="/article-0-{{ arg_dict.category_id }}.html">全部</a> 9 {% else %} 10 <a href="/article-0-{{ arg_dict.category_id }}.html">全部</a> 11 {% endif %} 12 :return: 13 ''' 14 ret = '' 15 if k == 'article_type_id': 16 n1 = arg_dict['article_type_id'] 17 n2 = arg_dict['category_id'] 18 if n1 == 0: 19 ret = '<a class="active" href="/article-0-%s.html">全部</a>' % n2 20 else: 21 ret = '<a href="/article-0-%s.html">全部</a>' % n2 22 else: 23 n1 = arg_dict['category_id'] 24 n2 = arg_dict['article_type_id'] 25 if n1 == 0: 26 ret = '<a class="active" href="/article-%s-0.html">全部</a>' % n2 27 else: 28 ret = '<a href="/article-%s-0.html">全部</a>' % n2 29 return mark_safe(ret) 30 @register.simple_tag 31 def filter_article_type(k,arg_dict,d): 32 ''' 33 {% for row in article_type_list %} 34 {% if row.id == arg_dict.article_type_id%} 35 <a class="active" href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a> 36 {% else %} 37 <a href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a> 38 {% endif %} 39 {% endfor %} 40 :return: 41 ''' 42 ret = [] 43 if d == 'article_type_id': 44 for row in k: 45 if row.id == arg_dict['article_type_id']: 46 temp = '<a class="active" href="/article-%s-%s.html">%s</a>'%(row.id,arg_dict['category_id'],row.caption) 47 else: 48 temp = '<a href="/article-%s-%s.html">%s</a>'%(row.id,arg_dict['category_id'],row.caption) 49 ret.append(temp) 50 else: 51 for row in k: 52 if row.id == arg_dict['category_id']: 53 temp = '<a class="active" href="/article-%s-%s.html">%s</a>'%(arg_dict['article_type_id'],row.id,row.caption) 54 else: 55 temp = '<a href="/article-%s-%s.html">%s</a>'%(arg_dict['article_type_id'],row.id,row.caption) 56 ret.append(temp) 57 58 return mark_safe(''.join(ret))
3、数据存在内存中从而少一张表
1 from django.db import models 2 3 # Create your models here. 4 class Category(models.Model): 5 caption = models.CharField(max_length=16) 6 # class ArticleType(models.Model): 7 # caption = models.CharField(max_length=16) 8 class Article(models.Model): 9 title = models.CharField(max_length=32) 10 content = models.CharField(max_length=255) 11 category = models.ForeignKey(Category,on_delete=models.CASCADE) 12 # article_type = models.ForeignKey(ArticleType,on_delete=models.CASCADE) 13 type_choice = ( 14 (1,'Python'), 15 (2,'OpenStack'), 16 (3,'Linux'), 17 ) 18 article_type_id = models.IntegerField(choices=type_choice)
from django import template from django.utils.safestring import mark_safe register = template.Library() @register.simple_tag def filter_all(arg_dict,k): ''' {% if arg_dict.article_type_id == 0 %} <a class="active" href="/article-0-{{ arg_dict.category_id }}.html">全部</a> {% else %} <a href="/article-0-{{ arg_dict.category_id }}.html">全部</a> {% endif %} :return: ''' ret = '' if k == 'article_type_id': n1 = arg_dict['article_type_id'] n2 = arg_dict['category_id'] if n1 == 0: ret = '<a class="active" href="/article-0-%s.html">全部</a>' % n2 else: ret = '<a href="/article-0-%s.html">全部</a>' % n2 else: n1 = arg_dict['category_id'] n2 = arg_dict['article_type_id'] if n1 == 0: ret = '<a class="active" href="/article-%s-0.html">全部</a>' % n2 else: ret = '<a href="/article-%s-0.html">全部</a>' % n2 return mark_safe(ret) @register.simple_tag def filter_article_type(k,arg_dict,d): ''' {% for row in article_type_list %} {% if row.id == arg_dict.article_type_id%} <a class="active" href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a> {% else %} <a href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a> {% endif %} {% endfor %} :return: ''' ret = [] if d == 'article_type_id': for row in k: if row[0] == arg_dict['article_type_id']: temp = '<a class="active" href="/article-%s-%s.html">%s</a>'%(row[0],arg_dict['category_id'],row[1]) else: temp = '<a href="/article-%s-%s.html">%s</a>'%(row[0],arg_dict['category_id'],row[1]) ret.append(temp) else: for row in k: if row.id == arg_dict['category_id']: temp = '<a class="active" href="/article-%s-%s.html">%s</a>'%(arg_dict['article_type_id'],row.id,row.caption) else: temp = '<a href="/article-%s-%s.html">%s</a>'%(arg_dict['article_type_id'],row.id,row.caption) ret.append(temp) return mark_safe(''.join(ret))
1 from django.shortcuts import render 2 from app01 import models 3 # Create your views here. 4 def article(request,*args,**kwargs): 5 # request.path_info #获取当前url 6 # from django.urls import reverse 7 # 8 # reverse('article',kwargs=kwargs) 9 # print(kwargs) 10 # article_type_list = models.ArticleType.objects.all() 11 article_type_list = models.Article.type_choice 12 category_list = models.Category.objects.all() 13 condition ={} 14 for k,v in kwargs.items(): 15 kwargs[k] = int(v) 16 if v == '0': 17 pass 18 else: 19 condition[k] = v 20 result = models.Article.objects.filter(**condition) 21 return render( 22 request, 23 'article.html', 24 { 25 'result':result, 26 'article_type_list':article_type_list, 27 'category_list':category_list, 28 'arg_dict':kwargs 29 })
二十一、JSONP跨域请求
1、JSONP原理
跨域的安全限制都是对浏览器端来说的,服务器端是不存在跨域安全限制的。
浏览器的同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。
如果协议,端口和主机对于两个页面是相同的,则两个页面具有相同的源,否则就是不同源的。
如果要在js里发起跨域请求,则要进行一些特殊处理了。或者,你可以把请求发到自己的服务端,再通过后台代码发起请求,再将数据返回前端。
同源策略:阻止Ajax请求,无法阻止<script src="..."></script>
因此:
-创建script标签
-src=远程地址
-返回的数据必须是js格式
JSONP只能发送GET请求不能发送POST请求
2、示例:通过原生的js去写
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h1>后台获取的结果</h1> 9 {{ result }} 10 <h1>js直接获取的结果</h1> 11 <input type="button" value="获取数据" onclick="getContent();"/> 12 <div id = 'container'></div> 13 <script> 14 function getContent() { 15 /* 16 var xhr = new XMLHttpRequest(); 17 xhr.open('GET',''); 18 xhr.onreadystatechange = function () { 19 console.log(xhr.responseText) 20 }; 21 xhr.send(); 22 */ 23 var tag = document.createElement('script'); 24 tag.src = 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403'; 25 document.head.appendChild(tag); 26 document.head.removeChild(tag); 27 } 28 function list(arg) { 29 console.log(arg) 30 } 31 </script> 32 </body> 33 </html>
3、jQuery封装之后
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h1>后台获取的结果</h1> 9 {{ result }} 10 <h1>js直接获取的结果</h1> 11 <input type="button" value="获取数据" onclick="getContent();"/> 12 <div id = 'container'></div> 13 <script src="/static/jquery-1.12.4.js"></script> 14 <script> 15 function getContent() { 16 /* 17 var xhr = new XMLHttpRequest(); 18 xhr.open('GET',''); 19 xhr.onreadystatechange = function () { 20 console.log(xhr.responseText) 21 }; 22 xhr.send(); 23 */ 24 /* 25 var tag = document.createElement('script'); 26 tag.src = 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403'; 27 document.head.appendChild(tag); 28 document.head.removeChild(tag);*/ 29 $.ajax({ 30 url:'http://www.jxntv.cn/data/jmd-jxtv2.html', 31 type:'POST', 32 dataType:'jsonp', 33 jsonp:'callback', 34 jsonpCallback:'list' 35 36 }) 37 38 } 39 function list(arg) { 40 console.log(arg) 41 } 42 </script> 43 </body> 44 </html>
另一种方式:CORS跨站资源共享:设置响应头
新版本Django错误
django.core.exceptions.ImproperlyConfigured: Application labels aren't unique, duplicates: XXX(application name)
解决方法
创建的应用中settings.py文件INSTALLED_APPS注册文件按之前手动自行注册了应用名称。
更新django1.11.3后,django创建应用的时候已经帮你注册了xx.apps.XXConfig了。
...