python笔记-19 javascript补充、web框架、django基础
一、JavaScript的补充
1 正则表达式
1.1 test的使用
test 测试是否符合条件 返回true or false
1.2 exec的使用
exec 从字符串中截取匹配的字符
1.3 分组
-/g /m /i 分组
JavaScript 正则表达式
- test 测试是否符合条件 返回true or false
- exec 从字符串中截取匹配的字符
test 判断字符串是否包含数字 判断字符串是否全是数字 var pattern=/\d+/; pattern.test('aaa123bbb'); true var pattern=/^\d+$/; pattern.test('aaa123bbb'); false exec 截取数字 rep = /\d+/; str = "wangshen_67_houyafa_20"; rep.exec(str); ["67", index: 9, input: "wangshen_67_houyafa_20", groups: undefined] 分组 pattern=/Java(\w*)/g; /Java(\w*)/g str="JavaScript is more fun than Java or JavaBeans!"; "JavaScript is more fun than Java or JavaBeans!" pattern.exec(str); (2) ["JavaScript", "Script", index: 0, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] pattern.exec(str); (2) ["Java", "", index: 28, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] pattern.exec(str); (2) ["JavaBeans", "Beans", index: 36, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] \g 是一个迭代器,每运行一次生成下一个 pattern.exec(str); ["JavaScript", index: 0, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] pattern.exec(str); ["Java", index: 28, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] pattern.exec(str); ["JavaBeans", index: 36, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] pattern.exec(str); null pattern.exec(str); ["JavaScript", index: 0, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] pattern.exec(str); ["Java", index: 28, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] pattern.exec(str); ["JavaBeans", index: 36, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] pattern.exec(str); null pattern.exec(str); ["JavaScript", index: 0, input: "JavaScript is more fun than Java or JavaBeans!", groups: undefined] var pattern = /\d+/g; undefined str='aaa123bbb345'; "aaa123bbb345" pattern.exec(str); ["123", index: 3, input: "aaa123bbb345", groups: undefined] pattern.exec(str); ["345", index: 9, input: "aaa123bbb345", groups: undefined] pattern.exec(str); null pattern.exec(str); ["123", index: 3, input: "aaa123bbb345", groups: undefined] pattern.exec(str); ["345", index: 9, input: "aaa123bbb345", groups: undefined] str="JavaScript is more fun than xxxJavaxxx or JavaBeans!"; "JavaScript is more fun than xxxJavaxxx or JavaBeans!" pattern.exec(str); null pattern.exec(str); (2) ["JavaScript", "Script", index: 0, input: "JavaScript is more fun than xxxJavaxxx or JavaBeans!", groups: undefined] pattern.exec(str); (2) ["Javaxxx", "xxx", index: 31, input: "JavaScript is more fun than xxxJavaxxx or JavaBeans!", groups: undefined] pattern.exec(str); (2) ["JavaBeans", "Beans", index: 42, input: "JavaScript is more fun than xxxJavaxxx or JavaBeans!", groups: undefined] pattern.exec(str); null pattern=/\bJava(\w*)\b/g; /\bJava(\w*)\b/g pattern.exec(str); (2) ["JavaScript", "Script", index: 0, input: "JavaScript is more fun than xxxJavaxxx or JavaBeans!", groups: undefined] pattern.exec(str); (2) ["JavaBeans", "Beans", index: 42, input: "JavaScript is more fun than xxxJavaxxx or JavaBeans!", groups: undefined] pattern.exec(str); null pattern.exec(str); (2) ["JavaScript", "Script", index: 0, input: "JavaScript is more fun than xxxJavaxxx or JavaBeans!", groups: undefined] pattern.exec(str); (2) ["JavaBeans", "Beans", index: 42, input: "JavaScript is more fun than xxxJavaxxx or JavaBeans!", groups: undefined] \b的使用 \i 不区分大小写 \m 多行的使用 默认是多行匹配 即支持多行寻找 只是针对^$时候,对于多行的处理 str="JavaScript is more fun than \nJava or JavaBeans!" "JavaScript is more fun than Java or JavaBeans!" rep=/^Java\w*/g; /^Java\w*/g rep.exec(str); ["JavaScript", index: 0, input: "JavaScript is more fun than ↵Java or JavaBeans!", groups: undefined] rep.exec(str); null rep=/^Java\w*/mg; /^Java\w*/gm rep.exec(str); ["JavaScript", index: 0, input: "JavaScript is more fun than ↵Java or JavaBeans!", groups: undefined] rep.exec(str); ["Java", index: 29, input: "JavaScript is more fun than ↵Java or JavaBeans!", groups: undefined] rep.exec(str); null
2、js与jq的一些补充
2.1 js与jq的绑定事件的方式的不通(是否有on)
2.2 return 方式来阻塞后续事件的发生(此时不展开,在上一篇文章中有提到)
2.3 注意submit 与checkbook事件执行的先后顺序(绑定click事件)
submit动作 执行先后
checkbok的事件 执行先后
实例;表单验证
3、前端组件
实际就是别人写好了模板,样式,做个了结,在后面的项目部分,或使用这些个前端组件,进行快速的前端开发
几个比较出名的组件
easyui
jqueryui
bootstrap
组件的补充内容
3.1、响应式 @media 根据web界面的长框等因素使用不同的样式。即自适应
3.2、字体图标 以bootstrap为例,bootstrap在使用图标的时候应该注意到字体文件的导入。(之前博客的font awesome图标不要导入字体)
3.3、滚动图 bxslider
二、web框架的本质与wsgi
1、web框架的本质就是socket *
import socket server=socket.socket() server.bind(('0.0.0.0',10086)) server.listen(5) while True: conn,add=server.accept() tmp_recv=conn.recv(1024) conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send('hello world,你好世界'.encode('gbk'))#此处如果用utf-8 那么浏览器会显示什么 conn.close()
-------------------
print(tmp_recv)
b'
GET / HTTP/1.1\r\n
Host: 127.0.0.1:10086\r\n
Connection: keep-alive\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: csrftoken=VBVNk321AAGMuygu2OWoRXHUc2LiIspKoPaQfN1AbSY2WEUWPQS09ghoop1LzXFw; sessionid=os5tnm6lqwxxiill5py3ylzrbcs0j2hb\r\n
Accept-Encoding: gzip, deflate\r\n\r\n
'
b'
GET /favicon.ico HTTP/1.1\r\n
Host: 127.0.0.1:10086\r\n
Connection: keep-alive\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n
Accept: image/webp,image/apng,image/*,*/*;q=0.8\r\n
Referer: http://127.0.0.1:10086/\r\nAccept-Language: zh-CN,zh;q=0.9\r\n
Cookie: csrftoken=VBVNk321AAGMuygu2OWoRXHUc2LiIspKoPaQfN1AbSY2WEUWPQS09ghoop1LzXFw; sessionid=os5tnm6lqwxxiill5py3ylzrbcs0j2hb\r\n
Accept-Encoding: gzip, deflate\r\n\r\n
'
我们可以使用decode对byte类型的数据进行加工。此处不做深入,上述过程只为了说明一点,web框架的本质就是byte类型的数据交互的过程。我们可以处理分析对方发过来的byte内容,然后处理后回复不通类型的数据
2、wsgi -python中的wsgi模块可以替我们搭建web框架
上面我们说到了web框架的本质,是处理b类型的数据和发送b类型的数据,在socket建立的情况下。
但是,此时我们需要明确,交互的数据有交互的规范。每种请求,不同状态码代表的含义,包头需要涵盖的内容等,这些东西我们都要去做处理分析。
这中间需要大量的人力投入,去开发一套稳定成熟的web框架。而此时,我们需要在web框架上搭建的网站,平台,系统等东西却还都没有开始开工。
现在市面上有大量成熟的web框架供我们去使用,这里就节省了我们去开发web框架和专门研究交互报文的复杂过程。更快的投入入高效的开发
2.1 wsgivef.simple_server 的使用
了解了HTTP协议和HTML文档,我们其实就明白了一个Web应用的本质就是:
-
浏览器发送一个HTTP请求;
-
服务器收到请求,生成一个HTML文档;
-
服务器把HTML文档作为HTTP响应的Body发送给浏览器;
-
浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。
-
所以,最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。Apache、Nginx、Lighttpd等这些常见的静态服务器就是干这件事情的。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。
这个接口就是WSGI:Web Server Gateway Interface。
使用wsgi相比于上面的直接建立socket,wsgi省去了建立socket监听端口这个过程
但是我们在使用wsgi时候还需要去考虑http的返回值
from wsgiref.simple_server import make_server def xx(environ,start_response): #environ封装所有客户端发过来的信息 start_response('200 OK',[('Content-Type','text/html')]) return ['<h1>yes ok!</h1>'.encode('utf-8')] if __name__=='__main__': httpd=make_server('',8000,xx) print('start listen') httpd.serve_forever() 提升一 如何处理多个不同的url from wsgiref.simple_server import make_server def deal_index(): return ['<h1>index</h1>'.encode('utf-8')] def deal_login(): return ['<h1>login</h1>'.encode('utf-8')] def deal_register(): return ['<h1>register</h1>'.encode('utf-8')] url_dict={ '/index':deal_index, '/login':deal_login, '/register':deal_register } def deal_conn(environ,start_response): #environ封装所有客户端发过来的信息 start_response('200 OK',[('Content-Type','text/html')]) tmp_path=environ['PATH_INFO'] func=None if tmp_path in url_dict: func=url_dict[tmp_path] if func: return func() else: return ['<h1>404</h1>'.encode('utf-8')] if __name__=='__main__': httpd=make_server('',8000,deal_conn) print('start listen') httpd.serve_forever() 提升二 将页面移出去->view中 ->页面模板 将处理url的函数移出去放到->controller中 ->业务处理 将数据库操作移动到->model中
我们可以根据发过来的 请求做相应的回复,如200,400,500等
同时,为了规范代码我们可以将页面移动到view中,数据库操作移动到model中,url控制移动到controller中,
这样 三个文件夹就组成了mvc架构
3、由wsgi模块引出的mvc架构与mtv架构
从上文我们知道
web框架本质->socket
wsgi->封装socket->是我们在编程时更专注于web,无需考虑socket,但是需要考虑返回值
wsgi中->规范三个文件夹 model,view,controller 组成mvc架构
同时,和mvc结构类似的有mtv架构 实际 model都是数据库操作,template中存放页面web,(类似于mvc中的v),view中存放url选路的操作(类似于mvc中的controller)
3、django的引入
由于wsgi中还需要我们去考虑返回值等情况,为了更便于专注web的开发,减少http头部返回值等信息的开发实际,
在python中又出现了再wsgi之上的封装的web框架
Django:全能型Web框架;
web.py:一个小巧的Web框架;
Bottle:和Flask类似的Web框架;
Tornado:Facebook的开源异步Web框架。
uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。WSGI是一种Web服务器网关接口。它是一个Web服务器(如nginx,uWSGI等服务器)与web应用(如用Flask框架写的程序)通信的一种规范。
要注意 WSGI / uwsgi / uWSGI 这三个概念的区分。
WSGI是一种通信协议。
uwsgi是一种线路协议而不是通信协议,在此常用于在uWSGI服务器与其他网络服务器的数据通信。
而uWSGI是实现了uwsgi和WSGI两种协议的Web服务器。
综上:wsgi处理socket,我们还可以使用uwsgi来和nginx进行对接处理高并发的情况。在wsgi基础上,为了减少我们对返回值,url路径分析的处理时间,更高效的进行开发。在wsgi基础上又出现了django,tornado,flask等框架。
三、django入门
1、创建工程
1.1 命令行创建
#创建项目 D:\Python36\Scripts>django-admin.exe startproject myxxx #运行项目 D:\Python36\Scripts\myxxx>python manage.py runserver Performing system checks... System check identified no issues (0 silenced). You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. Run 'python manage.py migrate' to apply them. August 14, 2018 - 15:25:57 Django version 2.1, using settings 'myxxx.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CTRL-BREAK.
我们在runserver的时候可以指定ip和端口号 G:\mytest>python manage.py runserver 127.0.0.1:8888
1.2 pycharm创建
1.3 pycharm使用的一些注意点
新建django项目时报错
django1.x 和django2.x的变化,实际变化不大。后面使用过程中会有深入理解
1.x的url与2.x的path及re_path
2、django项目的目录结构
具体目录的功能会在下文实际应用过程中有更深刻的理解
目录结构 G:\MYTEST │ db.sqlite3 │ manage.py │ └─mytest │ settings.py │ urls.py │ wsgi.py │ __init__.py │ └─__pycache__ settings.cpython-36.pyc urls.cpython-36.pyc wsgi.cpython-36.pyc __init__.cpython-36.pyc G:\>
3、app的理解
app相当于一个大项目的小模块,一个大项目可以有多个模块组成,有不同的团队负责开发。将每个模块分别独立出来进行开发调试,能提高工作效率且便于项目的管理
3.1 app的创建
F:\django_test>python manage.py startapp CMDB
F:\django_test>python manage.py startapp openstack
3.2 app的目录结构
3.3 文件功能
典型的一个mvc架构
models提供数据库相关的操作
views用来对请求做处理,处理业务逻辑
templates用来存放web界面
4、实例:回复浏览器发送的请求一个html界面的方法
4.1 通过HttpResponse
f=open('templates\login.html','rb') data=f.read() return HttpResponse(data)
4.2 通过render render的本质就是对HttpResponse的进一步封装
from django.shortcuts import render return render(request,'login.html')
4.3 render的setting配置(目的告诉django你的templates在哪里)
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS':[os.path.join(BASE_DIR, 'templates'),], #添加这一句 'APP_DIRS': True,
4.4 redirect的本质
回复302的报文,并添加Location字段,ajax对此302的redirect不做反应
5、静态文件
对于web中要使用的静态请求内容,一般不做views处理,将其配置成静态文件来处理。即访问这个url下的东西,直接是去获取文件。
STATIC_URL = '/static/' STATICFILES_DIRS=( os.path.join(BASE_DIR,'static'), )
6、模板语言的简单应用及请求内容的获取: 实例:完成简单的用户登录及界面跳转
功能:打开url:xxx/login/ 显示web,输入用户名密码,如果不正确则报错
6.1 urls 请求的解析
6.2 views 请求的处理、获取请求内包含的内容
def login(request): if request.method=='POST': t_n=request.POST.get('name',None) t_p=request.POST.get('passwd',None) t_n=str(t_n) t_p=str(t_p) print(t_n) print(t_p) if t_n=='root' and t_p=='123': return redirect('/home/') else: return render(request, 'login.html',{'error_msg':'用户名或密码错误'}) return render(request,'login.html')
6.3 模板语言使用 渲染html文件并交给views返回
7、简要了解Django的生存周期
7.1 收到请求
7.2 路由系统urls 解析发送过来的请求
7.3 视图函数 urls路由系统解析后,调用相应的视图函数对请求进行处理
7.4 界面渲染 调用配置文件或request请求的内容,或数据库内容,对web界面进行渲染,动态生成需要返回给用户的界面的过程
7.5 返回用户
#注意以上的请求生存周期只是简单的概括,后续在中间件环境还会对生存周期做相应的扩展。
8、概括上述django入门的相关内容
1. 创建Django工程 django-admin startproject 工程名 2. 创建APP cd 工程名 python manage.py startapp cmdb 3、静态文件 project.settings.py STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), ) 4、模板路径 DIRS ==> [os.path.join(BASE_DIR,'templates'),] 5、settings中 middlerware # 注释 csrf 6、定义路由规则 url.py "login" --> 函数名 7、定义视图函数 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') -》重新请求到新地址 本地地址前面的/必须填 8、模板渲染(视图函数) 特殊的模板语言 静态文件->动态页面
四、django获取请求内容的方式及CBV/FBV
1、获取请求方式
if request.method=='POST': if request.method=='GET':
2、获取请求的url
request.path
request.path_info **常用
3、获取get或post的内容
if request.method=='GET': print('GET',request.GET) print('POST',request.POST) ------------------------------- GET <QueryDict: {}> POST <QueryDict: {'question1': ['1-B', '1-C', '1-D']}> ------------------------------ request.GET.get('key') request.POST.get('key')
4、一个变量多个值的处理
def home(request): if request.method=='POST': print('GET',request.GET) print('POST',request.POST) print('POSTLIST',request.POST.getlist('question1')) obj=request.POST.getlist('question1') for i in obj: print(i) return render(request, 'home.html') if request.method=='GET': print('GET',request.GET) print('POST',request.POST) return render(request,'home.html') GET <QueryDict: {}> POST <QueryDict: {'question1': ['1-B', '1-C', '1-D']}> POSTLIST ['1-B', '1-C', '1-D'] 1-B 1-C 1-D [15/Aug/2018 16:39:46] "POST /home/ HTTP/1.1" 200 968 #此处需要注意变量取出来的列表逐一输出的方法和模板语言的写法
5、接收文件的处理
5.1 获取文件 x=request.Files.get()
5.2 获取文件名 x.name
5.3 循环接收文件 chunks
def home(request): if request.method=='POST': tmp_file=request.FILES.get('test_paper') if tmp_file: f=open(tmp_file.name,'wb') for i in tmp_file.chunks(): f.write(i) f.close() for i in obj: print(i) return render(request, 'home.html')
6、CBV与FBV
6.1 FBV views中定义一个函数来处理请求 此处不做展开
6.2 CBV views中定义一个类来处理请求
6.3 urls中的调用
6.4 class的定义 继承View
from django.views import View class Index(View): def get(self, req): print(‘method is :‘ + req.method) return render(req, ‘index.html‘) def post(self, req): print(‘method is :‘ + req.method) return render(req, ‘index.html‘) ----------------------------- urlpatterns = [ # url(r‘^index/‘, views.index), url(r‘^index/‘, views.Index.as_view()),
6.5 dispatch的使用 此方法在后面对界面做登录检测的时候会体现出装饰器对cbv和fbv的区别
class cbv2(View): def dispatch(self, request, *args, **kwargs): print('before') t_return=super(cbv2, self).dispatch(request,*args,**kwargs) print('after') return t_return def get(self,request): return render(request,'home.html') def post(self,request): return HttpResponse('cbv post') Starting development server at http://127.0.0.1:8000/ Quit the server with CTRL-BREAK. before after
五、django的模板语言
要区分{{ }}与{% %}
1、普通变量
def func(request): return render(request, "index.html", {'current_user': "aaaa"}) --------------------------------------------------------------------- <html> <body> <div>{{current_user}}</div> </body> </html> ====> 最后生成的字符串 <html> <body> <div>aaaa</div> </body> </html>
2、for循环
def func(request): return render(request, "index.html", {'current_user': "alex", 'user_list': ['alex','eric']}) -------------------------------------------------------------------------------- <html> <body> <div>{{current_user}}</div> <ul> {% for row in user_list %} {% if row == "alex" %} <li>{{ row }}</li> {% endif %} {% endfor %} </ul> </body> </html>
3、索引
通过下标取值
#####索引################# (传入字典、列表取值的处理方法) def func(request): return render(request, "index.html", { 'current_user': "alex", 'user_list': ['alex','eric'], 'user_dict': {'k1': 'v1', 'k2': 'v2'}}) index.html <html> <body> <div>{{current_user}}</div> <a> {{ user_list.1 }} </a> <a> {{ user_dict.k1 }} </a> <a> {{ user_dict.k2 }} </a> </body> </html>
4、条件判断
def func(request): return render(request, "index.html", { 'current_user': "alex", "age": 18, 'user_list': ['alex','eric'], 'user_dict': {'k1': 'v1', 'k2': 'v2'}}) index.html <html> <body> <div>{{current_user}}</div> <a> {{ user_list.1 }} </a> <a> {{ user_dict.k1 }} </a> <a> {{ user_dict.k2 }} </a> {% if age %} <a>有年龄</a> {% if age > 16 %} <a>老男人</a> {% else %} <a>小鲜肉</a> {% endif %} {% else %} <a>无年龄</a> {% endif %} </body> </html>
5、再来一则例子
Html文件 {%for item in user_list%} <tr> <td>{{item.name}}</td> <td>{{item.sex}}</td> <td>{{item.email}}</td> </tr> {%endfor%} Views中 user_list=[ {'name':'a001', 'sex':'男','email':'a001@163.com'}, {'name':'a002', 'sex':'男','email':'a002@163.com'}, {'name':'a003', 'sex':'男','email':'a003@163.com'}, {'name':'a004', 'sex':'男','email':'a004@163.com'}, ] return render(request, 'home.html',{'user_list':user_list}) 字典处理 View中 user_dict={ 'k1':{'name': 'a001', 'sex': '男', 'email': 'a001@163.com'}, 'k2':{'name': 'a002', 'sex': '男', 'email': 'a002@163.com'}, 'k3':{'name': 'a003', 'sex': '男', 'email': 'a003@163.com'}, 'k4':{'name': 'a004', 'sex': '男', 'email': 'a004@163.com'}, } def deal_dict(request): return render(request,'dict.html',{'user_dict':user_dict}) Html中 <ul> {% for x in user_dict.keys %} <li>{{x}}</li> {% endfor %} <div>--------</div> {% for y in user_dict.values %} <li>{{y}}</li> {% endfor %} <div>--------</div> {% for z,zz in user_dict.items %} <li>{{z}}--{{zz}}</li> {% endfor %} </ul>
6、实例:查看账户详细信息
views
def detail(request): nid=request.GET.get('nid') return render(request,'detail.html',{'user_detail':user_dict[nid]}) dict页面 {% for z,zz in user_dict.items %} <li><a href="/detail?nid={{z}}" target="_blank">{{zz.name}}</a></li> {% endfor %} Detail页面 <body> <div>详细信息</div> <div>姓名:{{user_detail.name}}</div> <div>性别:{{user_detail.sex}}</div> <div>邮箱:{{ user_detail.email }}</div> </body>
六、路由时正则表达式的使用、模板中url路径的生成、路由分发
1、re_path正则表达式的使用
1.1 分组
urlpatterns = [ re_path('newdetail-(\d+)-(\d+).html', views.newdetail) ]
1.2 分组并指定变量名
re_path('newdetail-(?P<uid>\d+)-(?P<uuid>\d+).html', views.newdetail)
1.3 view中接收参数的方式
def newdetail(request,x1,x2):顺序接收
def newdetail(request,nid,nnid):按变量名接收
def newdetail(request,*args,**kwargs):多变量接收
2、templates中生成url的方法
2.1 request.path_info
2.2 url name别名
2.2.1 确定值
url中 re_path('namexxx',views.deal_name,name='tmp_name') Html中 <body> {%url 'tmp_name'%} </body>
2.2.2 搭配正则
<body> {%url 'tmp_name' 123 456%} </body>
2.3 reverse
此处不做描述,不常见,知道有这个方式即可
3、路由分发
实际就是将总的项目文件夹中的urls拆分到各个app中的urls中
需要使用include
七、django的数据库操作-orm
对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping)
1、django默认的数据库sql-lite的操作
1.1 python manage.py makemigratations
1.2 python manage.py migrate
直接执行上面两个指令,就能在项目的文件夹下发出现一个sqllite的文件,这就是django默认的数据库了
2、django使用mysql的方法
2.1 setting中注册
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django_1', 'HOST': '192.168.106.128', 'PORT': '3306', 'USER': 'root', 'PASSWORD': '123456', } }
2.2 init中注册 注意要先安装pymysql
import pymysql pymysql.install_as_MySQLdb()
3、创建自己的表
3.1定义表
#在models中操作 from django.db import models class User_info(models.Model): name=models.CharField(max_length=32) passwd=models.CharField(max_length=64) class xxx(models.Model): name=models.CharField(max_length=32) passwd=models.CharField(max_length=64)
3.2 注册表 在setting中
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app1', ]
3.3执行生成表
3.3.1 python manage.py makemigratations
3.3.2 python manage.py migrate
D:\untitled>python manage.py makemigrations Migrations for 'app1': app1\migrations\0001_initial.py - Create model User_info - Create model xxx D:\untitled>python manage.py migrate
4、单表的增删改查
4.1 增加
3种新增记录的方法 ->models.xxx.obj.create(k1=xxx,k2=xxx) ->models.xxx.obj.create(**xxx_dict) ->tmp_obj=models.xxx(k1=xxx,k2=xxx) tmp_obj.save()
4.2 查
4.2.1 全查 all()
4.2.2 过滤 filter() 过滤可以使用q语句,在后面的项目中会具体展开
全部查 all 过滤 filter 一个条件 多个条件and关系 逗号 result=models.User_info.objects.all() for row in result: print(row.id,row.name,row.passwd) result1=models.User_info.objects.filter(name='xiaoming',passwd='aaaaaa') print('------分割线-----') for row in result1: print(row.id,row.name,row.passwd) return HttpResponse('orm') 1 tom 123456 2 alex abcdef 3 xiaoming aaaaaa 4 tom 123456 5 alex abcdef 6 xiaoming aaaaaa 7 tom 123456 8 alex abcdef 9 xiaoming aaaaaa ------分割线----- [17/Aug/2018 22:03:52] "GET /db_opt HTTP/1.1" 200 3 3 xiaoming aaaaaa 6 xiaoming aaaaaa 9 xiaoming aaaaaa
4.2.3 values 获得一个字典
4.2.4 values_list 获得一个列表
def db_search(request): s1=models.User_auth.objects.all() s2=models.User_auth.objects.all().values('name','password') s3=models.User_auth.objects.all().values_list('name','password') return render(request,'db_search.html',{'s1':s1,"s2":s2,"s3":s3}) --------------------------------------- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p> <div>对象</div> <ul> {% for i in s1 %} <li>{{i.name}}-{{ i.password}}</li> {% endfor %} </ul> </p> <p> <div>字典</div> <ul> {% for i in s2 %} <li>{{i.name}}-{{ i.password}}</li> {% endfor %} </ul> </p> <p> <div>列表</div> <ul> {% for i in s3 %} <li>{{i.0}}-{{ i.1}}</li> {% endfor %} </ul> </p> </body> </html> -------------------------------
models.User.objects.filter(id=1,name='root') models.User.objects.filter(id__gt=1,name='root') models.User.objects.filter(id__lt=1) models.User.objects.filter(id__gte=1) models.User.objects.filter(id__lte=1)
4.2.5 get 没有该值会报错(可以用try捕捉异常)
4.3 删除 delete
models.User_info.objects.filter(name='xiaoming', passwd='aaaaaa').delete()
4.4 改 update
models.User_info.objects.filter(id=4).update(name=444,passwd=444)
4.5 一个实例,用户信息管理
4.5.1 Html补充 Input 的 placeholder/a标签的text-decoration: none;/ style="cursor: pointer"
用户信息管理 from CMDB import models def init_db(request): # models.User_auth.objects.create(name='a001',password=123456) # models.User_auth.objects.create(name='a002', password='aaabbb') return render(request,'init_db.html') def c_login(request): if request.method=='POST': t_name=request.POST.get('name') t_passwd=request.POST.get('passwd') print(t_name,t_passwd) if (t_passwd and t_passwd): t_auth_result=models.User_auth.objects.filter(name=t_name,password=t_passwd).first() if t_auth_result: return redirect('/cmdb/c_index') else: return render(request, 'c_login.html', {'error_msg': '用户名或密码错误'}) else: return render(request,'c_login.html',{'error_msg':'不能为空'}) return render(request,'c_login.html',{'error_msg':''}) def c_index(request): return render(request,'c_index.html') def c_user_manage(request): if request.method=="POST" : t_name=request.POST.get('name') t_password=request.POST.get('password') if t_name and t_password: models.User_auth.objects.create(name=t_name,password=t_password) return redirect('/cmdb/c_user_manage') else: pass else: pass t_sql_result=models.User_auth.objects.all() return render(request,'c_user_manage.html',{'user_info':t_sql_result}) def c_user_detail(request,nid): t_sql_result=models.User_auth.objects.filter(id=nid).first() return render(request, 'c_user_detail.html', {'user_detail': t_sql_result}) def c_user_del(request,nid): t_sql_result=models.User_auth.objects.filter(id=nid).delete() return redirect('/cmdb/c_user_manage') def c_user_edit(request,nid): if request.method=="POST": t_name=request.POST.get('name') t_pass=request.POST.get('password') if t_name and t_pass: models.User_auth.objects.filter(id=nid).update(name=t_name,password=t_pass) return redirect('/cmdb/c_user_manage') else: pass t_sql_result = models.User_auth.objects.filter(id=nid).first() return render(request,'c_user_edit.html',{'user_edit':t_sql_result})
5、数据表结构的修改
5.1 增加列 (两个思路)
5.1.1 给默认值
5.1.2 留空
5.2 删除列 ->直接删除
5.3 修改 ->直接修改
5.4 新建表
默认的自增id 注意自增和主键的区别,设置primary_key后就不会出现默认的id了
class User_test(models.Model): nid=models.CharField(max_length=32,primary_key=True) name=models.CharField(max_length=32) #AutoField(Field)
5.5 同步修改到数据库
python manage.py makemigrations
python manage.py migrate
七、django的中数据库的数据类型
1、数据库中数据类型的分类
1.1 字符型
1.2 数字
1.3 时间
1.4 布尔值0/1
2、django中的数据类型和字段参数
有些数据类型和字段参数非数据库mysql本身的限制 而是djangoadmin中的限制
2.1 django admin的使用(账号的建立)
要测试djangoadmin 此处需要建立超级用户 (venv) F:\django_test>python manage.py createsuperuser Username (leave blank to use 'raytine'): root Email address: root@xxx.com Password: Password (again): This password is too short. It must contain at least 8 characters. This password is too common. This password is entirely numeric. Bypass password validation and create user anyway? [y/N]: y Superuser created successfully. (venv) F:\django_test>
另外还需要将自建的数据表注册到djangoadmin中
from CMDB.models import User_test admin.site.register(User_test)
2.2 django中的数据类型
以下只列出几个常用的
AutoField 用于存放 integer 类型的数字。 BooleanField 用于存放布尔类型的数据(Ture 或 False) CharField 用于存放字符型的数据,需要指定长度 max_length。 CommaSeparatedIntegerField 用于存放用逗号隔开的 integer 类型的数据。 DateField 日期型,必须是“YYYY-MM-DD”格式 DateTimeField 日期时间型,必须是"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] "格式。 DecimalField 小数型,用于存放小数的数字。 EmailField 电子邮件类型 FilePathField 文件路径类类型,FilePathFields must have either 'allow_files' or 'allow_folders' set to True. FloatField 浮点型。用于存放浮点型数据。 IntegerField 用于存放 integer 类型的数字。 BigIntegerField 用于存放大 integer 类型的数字,最大数支持:9223372036854775807 IPAddressField IP 地址类型,支持的 IPv4 的长度。 GenericIPAddressField 一般的 IP 地址,可以支持ipv4和v6 TextField 用于存放文本类型的数据。 TimeField 时间类型。"HH:MM[:ss[.uuuuuu]]" 格式 URLField 用于存放 URL 地址
Django admin中会对一些char varchar类型的字符 做相应的限制 如是否满足ip email特征这些。
2.3 参数类型
2.3.1 null null=True 是否为空
2.3.2 Default 默认值
2.3.3 Primary key 主键
2.3.4 db_column=’xxx’ 生成数据库表时列的名字
2.3.5 db_index 创建普通索引
2.3.6 unique 唯一索引
2.3.7 几种索引的理解
2.3.8 修改时间 auto_now
2.3.9 创建时间 auto_now_add
注意时间类型及修改时间生效的方式
obj=models.User_test.objects.get(name='test_update') obj.name='new_one' obj.save() 只能用get来修改才能生效
2.3.10 choice 将一部分信息放在内存中 属于django的操作
2.3.11 Blank ->django admin中能否为空 这个需要和数据库 null做对比,一个是数据库中的限制,一个是django中的限制
2.3.12 Editable ->django admin中能否被编辑 ,如果设置了不能被编辑 那么这个字段就从admin界面隐藏
2.3.13 verbose_name -> django admin显示字段中文
2.3.14 error_messages -> 错误信息
2.3.15 help_text -> django admin提示
2.3.16 validators -> django form ,自定义错误信息
八、django对数据库的一对多操作
1、如何建立一对多外键 ForeignKey
dev_type=models.ForeignKey("devtype_table",to_field='tid',default=1,on_delete=models.
2、外键字段再数据库中的保存形式 fieldname_id
我们在models中定义的字段名称,如果是foreignkey字段,在数据库中显示的字段名实际为fieldname_id,而直接的字段名,被用作表示对象
3、一对多的情况下如何添加数据
models.tb.object.create(name='root', user_group_id=1)
4、一对多的情况下获取数据的三种方式
4,1 点获取
4.2 values获取,此方法要配合双下划线一起使用
4.3 values_list,此方法同样要配合双下划线一起使用
a.b_obj.filedname
value_list('b_obj__fieldname')
5、forloop的使用
在{% for %}循环内部,可以访问一个名为forloop的模板变量。这个变量有若干属性,通过它们可以获知循环进程的一些信息。 5.1 forloop.counter forloop.counter 的值是一个整数,表示循环的次数。这个属性的值从 1 开始,因此第一次循环时,forloop.counter 等于 1 。 {% for item in todo_list %} <p>{{ forloop.counter }}: {{ item }}</p> {% endfor %}123 5.2 forloop.counter0 forloop.counter0 与 forloop.counter 类似,不过是从零开始的。第一次循环时,其值为 0 。 5.3 forloop.revcounter forloop.revcounter的值是一个整数,表示循环中剩余的元素数量。第一次循环时, forloop.revcounter 的值是序列中要遍历的元素总数。最后一次循环时, forloop.revcounter的值为 1 。 5.4 forloop.revcounter0 forloop.revcounter0 与 forloop.revcounter类似,不过索引是基于零的。第一次循环时, forloop.revcounter0的值是序列中元素数量减去一。最后一次循环时, forloop.revcounter0 的值为 0 。 5.5 forloop.first forloop.first 是个布尔值,第一次循环时为 True 。需要特殊处理第一个元素时很方便: {% for object in objects %} {% if forloop.first %} <li class="first"> {% else %} <li> {% endif %} {{ object }} </li> {% endfor %}123456789 5.6 forloop.last forloop.last是个布尔值,最后一次循环时为True 。经常用它在一组链接之间放置管道符号: {% for link in links %} {{ link }}{% if not forloop.last %} | {% endif %} {% endfor %}
6、ajax的应用
1、ajax的基本语法
$.ajax({ url: '/host', type: "POST", data: {'k1': 123,'k2': "root"}, success: function(data){ console.log(data) } })
2、ajax结合json的应用
$('#ajax_auth').click(function () { var t_ip=$('#top_add_dev_ip').val(); var t_port=$('#top_add_dev_port').val(); var t_type=$('#top_add_dev_type').val(); $.ajax({ url:'/ajax_auth', type:"POST", data:{'t_ip':t_ip,'t_port':t_port,'t_type':t_type}, //headers:{'X-CSRFtoken':$.cookie('csrftoken')}, //执行成功后自动执行此事件 success:function (r_data) { t_dict=JSON.parse(r_data); //JSON.stringify(t_dict) console.log(t_dict); if(t_dict['tag']===true){ location.reload(); }else { $('#top_error_info').text(t_dict['error']); } } }) }); -------------------------------------------- def ajax_edit_dev_info(requset): t_result = { 'tag' : True, 't_ip': None, 't_port': None, 't_type': None, } t_tag=requset.POST.get('tag') #print('---------》',json_data) #data=json.loads(json_data) t_did=requset.POST.get('did') if t_tag=='GET_DATA': result_dict=models.dev_info.objects.filter(did=t_did).values('did','dev_ip','dev_port','dev_type_id').first() t_result['did']=result_dict['did'] t_result['t_ip']=result_dict['dev_ip'] t_result['t_port'] = result_dict['dev_port'] t_result['t_type'] = result_dict['dev_type_id'] else: t_result['tag']=False return HttpResponse(json.dumps(t_result))
九、django对数据库的多对多操作
1、自定义关系表
自己建立三张表,此方式比较灵活,各种功能可以自己控制,只需要使用一对多foreginkey的知识点
class book(models.Model): id=models.AutoField(primary_key=True) book_name=models.CharField(max_length=64) class author(models.Model): id=models.AutoField(primary_key=True) author_name=models.CharField(max_length=64) class custom_book_and_author(models.Model): book_obj=models.ForeignKey(to='book',to_field='id',on_delete=models.CASCADE) author_obj=models.ForeignKey(to='author',to_field='id',on_delete=models.CASCADE)
2、自动创建关系表
此方法建立的多对多会自动生成第三张表,且我们无法通过程序直接操作第三张表,但是可以通过两个manytomany的对象来间接操作第三张表
class book(models.Model): id=models.AutoField(primary_key=True) book_name=models.CharField(max_length=64) class author(models.Model): id=models.AutoField(primary_key=True) author_name=models.CharField(max_length=64) r=models.ManyToManyField('book')#自动创建
3、增删改查的实现
方式一,自定义关系表的情况下
可操作性空间大,但易用性比较麻烦
HostToApp.objects.create(hobj_id=1,aobj_id=2)
方式二 直接描述关系,间接操作第三张表,使用方便
obj = Application.objects.get(id=1) 增 obj.r.add(1) obj.r.add(2) obj.r.add(2,3,4) obj.r.add(*[1,2,3,4]) 删 obj.r.remove(1) obj.r.remove(2,4) obj.r.remove(*[1,2,3]) 清空 obj.r.clear() 改 obj.r.set([3,5,7])
获取queryset对象
obj.r.all()
obj.r.filter()
4、实例:实现模态对话框情况下ajax的添加
model -------------------------------------- class devtype_table(models.Model): tid=models.AutoField(primary_key=True) type_name=models.CharField(max_length=64) class dev_info(models.Model): did=models.AutoField(primary_key=True) dev_ip=models.CharField(max_length=32,unique=True) dev_port=models.CharField(max_length=32) dev_type=models.ForeignKey("devtype_table",to_field='tid',default=1,on_delete=models.CASCADE) -------------------------------------------- views --------------------------------------------- def c_dev_manage(request): dev_type_list=c_dev_select_list(request) if request.method=="POST" : t_ip=request.POST.get('dev_ip') t_port=request.POST.get('dev_port') t_type=request.POST.get('dev_type') print(t_ip,t_port,t_type) if t_ip and t_port and t_type: models.dev_info.objects.create(dev_ip=t_ip,dev_port=t_port,dev_type_id=t_type) return redirect('/cmdb/c_dev_manage') else: pass else: pass t_sql_result=models.dev_info.objects.all() return render(request,'c_dev_manage.html',{'dev_info':t_sql_result,'dev_type_list':dev_type_list,}) ---------------------------------------------- html ----------------------------------------------- <input type="button" class="m_button" value="添加"> <div id="top_login" class="top_login"> <div> <h3>设备添加</h3> <form action="/cmdb/c_dev_manage" method="POST"> <input type="text" placeholder="ip" name="dev_ip" id="top_add_dev_ip"> <input type="text" placeholder="post" name="dev_port" id="top_add_dev_port"> <select name="dev_type" id="top_add_dev_type"> <option value="1">路由器</option> <option value="2">交换机</option> <option value="3">防火墙</option> <option value="4">服务器</option> </select> <hr> <input id="top_add" type="submit" value="添加"> <input id="top_cancle" type="button" value="取消"> <a id="ajax_auth">ajax验证</a> </form> <div id="top_error_info" style="color: red"></div> </div> </div> <div id="shadow" class="shadow"></div> ------------------------------------------------------- css ------------------------------------------------------- .hide{ display: none; } .item{ display: block; text-align: center; text-decoration: none; color: white; } .shadow{ position: fixed; top: 0; right: 0; left: 0; bottom: 0; background-color: black; opacity: 0.6; z-index: 101; } .top_login{ position: fixed; width: 500px; height: 300px; top:50%; left: 50%; margin-left: -250px; margin-top: -150px; background-color: gainsboro; z-index: 102; padding: 10px; } ------------------------------------------- js -------------------------------------------- obj_list=document.getElementsByClassName('m_button'); for(var i =0;i<obj_list.length;i++){ obj_list[i].onclick= function () { document.getElementById('shadow').classList.remove('hide'); document.getElementById('top_login').classList.remove('hide') }; } ------------------------------------- $.ajax({ url:'/ajax_auth', type:"POST", data:{'t_ip':t_ip,'t_port':t_port,'t_type':t_type}, //headers:{'X-CSRFtoken':$.cookie('csrftoken')}, //执行成功后自动执行此事件 success:function (r_data) { t_dict=JSON.parse(r_data); //JSON.stringify(t_dict) console.log(t_dict); if(t_dict['tag']===true){ location.reload(); }else { $('#top_error_info').text(t_dict['error']); } } }) --------------------------------------- ajax验证 views def ajax_auth(request): t_result={ 'tag':True, 'error':None, 'data':None } t_ip=request.POST.get('t_ip') t_port=request.POST.get('t_port') t_type=request.POST.get('t_type') try: if t_ip and t_port and t_type: models.dev_info.objects.create(dev_ip=t_ip, dev_port=t_port, dev_type_id=t_type) else: t_result['tag']=False t_result['error']='输入的格式不对' except Exception as err: t_result['tag'] = False t_err=str(err) t_result['error'] =t_err print(t_result) return HttpResponse(json.dumps(t_result))
5、实例2 ajax模态编辑
重点是添加一个tag:mydid,以便后面编辑的时候调用
<a class="top_edit_label_a" mydid="3" style="cursor: pointer">模态编辑</a> #mydid 这一步是重点 ----------------------------- $('.top_edit_label_a').click( function () { var t_dict={ 'tag':'GET_DATA', 'did':null }; t_dict['did']=$(this).attr('mydid'); $('#shadow').removeClass('hide'); $('#top_edit').removeClass('hide'); console.log(t_dict); $.ajax({ url:'/ajax_edit_dev_info', type:'POST', data:t_dict, success:function (json_data) { data=JSON.parse(json_data); if (data['tag']){ $('#top_edit_dev_input_did').val(data['did']); $('#top_edit_dev_input_ip').val(data['t_ip']); $('#top_edit_dev_input_port').val(data['t_port']); $('#top_edit_dev_select_type').val(data['t_type']); }else{ location.reload(); } } }) } ); -------------------------------------- def ajax_edit_dev_info(requset): t_result = { 'tag' : True, 't_ip': None, 't_port': None, 't_type': None, } t_tag=requset.POST.get('tag') #print('---------》',json_data) #data=json.loads(json_data) t_did=requset.POST.get('did') if t_tag=='GET_DATA': result_dict=models.dev_info.objects.filter(did=t_did).values('did','dev_ip','dev_port','dev_type_id').first() t_result['did']=result_dict['did'] t_result['t_ip']=result_dict['dev_ip'] t_result['t_port'] = result_dict['dev_port'] t_result['t_type'] = result_dict['dev_type_id'] else: t_result['tag']=False return HttpResponse(json.dumps(t_result))
6、ajax的补充,datatype、traditions、serialize
6.1 dateType:'JSON'
data=JSON.parse(json_data); #如果没有指定dataTYPE而接收到的数据是json数据,则需要json.parse(data)来处理。
如果指定里datatype为json那么,接收的data将被自动执行json.parse。
相当于datatype封装了json.parse
$.ajax({ url:'', type:'POST', data:'', dataType:'JSON', success:function (json_data) { json_data['xxxx'] }
6.2 traditions:true
假如接收到的数据中字典内嵌套了列表,那么ajax无法解析,需要添加traditions:true来支持
6.3 serialize()
对于一个form,我们可以获取到form 然后serialize()提交。简便操作
十、request对象所包含的信息
不难发现,我们不论获取什么信息如POST内容、GET内容、PATH路径等都是从request中获取,现有疑问,数据包交互的过程中,包头及包内还有很多其他的内容,我们如何去获取呢,能从request中去获取吗,有三个方法可以使用:request.environ/request.body/request.Meta
1、request.environ方法
request.environ可以获取数据包内其他的信息,包括django不会去处理的信息
django不会去处理request对象中所有的信息,它只帮我们处理了一些较常用的POST、GET动作等,如果涉及到其他关于数据包的操作,如获取请求的UA时,我们就需要去request.environ中自己去获取了。
request.environ是一个字典,我们通过for的方式查看内部所有的值,此处不展开了
2、request.Meta 请求头
3、request.body 请求内容
4、set_cookie所保存的位置
set_cookie是放置在请求头中的
5、响应头添加自定义字段的方法
response['k1']=v1
十一、模板操作
网页的标题、导航栏等结构,在多个页码都会显示,如果我们在写每个界面的时候重复写这些代码,效率很低。所以将重复代码做成模板,供其他页码调用
1、extends的使用
extends用于导入网页模板
{%extends 'test.html'%}
2、block的使用
block用于定义代码块,即我们需要在模板中规划出相应的空白block,并给其命名,在调用模板的html文件中,我们需要定义出有内容的block块,页面在渲染过程中就会将这些block块替换到模板的block块位置。
需要注意普通内容的处理以及js、css内容的处理
导入模板 {%extends 'test.html'%} 普通内容content {% block content %} <div>填充到模板</div> {%endblock%} js与css的处理 {% block css %} .c1{ } {%endblock%} {% block js %} {%endblock%}
3、include调用组件
一个html文件只能引用一个模板即一个extends,不能引用多个
对于一些很多页面都需要使用的小组件,我们可以将其单独写成一个html文件并使用include来调用
{% include 'tag.html' %}来使用
4、实例
>base <body> base {% block content %} {% endblock %} </body> ->main {% extends 'xxx_base.html' %} {% block content %} <div>it is content</div> {% for i in t_dic %} {% include 'xxx_include.html' %} {% endfor %} {% endblock %} ->include <div>打开图片www.{{ i }}.com</div> ----------------------------------------------------- base it is content 打开图片www.1.com 打开图片www.2.com 打开图片www.3.com 打开图片www.4.com 打开图片www.5.com
十二、自定义tag与filter
django在模板渲染时将前端web界面内容返回到python中,使用python做处理后再返回给前端
1、django在模板渲染中自带的一些filter
django中自带了一些filter处理前端的内容如
{{‘abc’|lower}} {{'<div>xxx</div>'|safe}}
我们可以自己定义这些方法来处理前端的内容
2、自定义simple_tag
@register.simple_tag与load
->@register.simple_tag的使用 {%mysimpleTag k1 k2%}
->思路
a. app下创建templatetags目录
b. 创建任意.py文件
c. 创建template对象
d. 装饰器装饰函数
@register.simple_tag
def func(a1,a2,a3....)
return "asdfasd"
e. settings中注册APP
f. 顶部 {% load xxoo %}
g. {% 函数名 arg1 arg2 %}
h. 特殊情况
TEMPLATES = [
{
'DIRS':[os.path.join(BASE_DIR, 'templates'),],
'libraries':{'local_simpletags': 'table_admin.templatetags.local_simpletags',}
},
]
缺点:
不能作为if条件
优点:
参数任意
->应用
->直接输出返回结果
->赋值给一个变量
{% deal_str all_dict.o as x %}
{% if i == x %}
2、自定义filter
@register.filter与load
->filter
a. app下创建templatetags目录
b. 任意xxoo.py文件
c. 创建template对象 register
d.
@register.filter
def func(a1,a2)
return "asdfasd"
e. settings中注册APP
f. 顶部 {% load xxoo %}
g. {{ 参数1|函数名:"参数二,参数三" }} {{ 参数1|函数名:数字 }}
缺点:
最多两个参数,不能加空格
优点:
能作为if条件
十三、分页功能的实现
1、阶段一 原理
1.1 列表切片与queryset切片
x[0,10]
x[11,20]
x[21,30]
1.2 切片中变量的引入
x[y-1,y*10]
1.3 通过请求传入变量的方法
将y这个变量赋值定义为get内的一个参数
xxx.com/?y=1
2、阶段二 基本功能实现
2.1 divmod的使用
-> >>> divmod(10,2)
(5, 0)
>>> divmod(10,6)
(1, 4)
2.2 a标签生成连接
总页码数=divmod(k1,k2)[0] 或 divmod(k1,k2)[0]+1 for i in range 生成 "<a herf='/?y=%s'>%s</a>"%(y,y)
2.3 xss攻击的处理
{{ html_str|safe }}
-------------------------------
from django.utils.safestring import mark_safe
tmpstr=make_safr(tmpstr)
{% extends "mod_all.html" %} {% block css %} <style> .page_num{ margin:0 5px; text-align: center; padding: 2px; width: 30px; display: inline-block; border: 1px solid deepskyblue; } .page_num.active{ background-color: skyblue; } </style> {% endblock %} {% block content %} <ul> {% for i in record_list %} <li>{{i}}</li> {% endfor %} </ul> {{ html_str|safe }} {% endblock %} ------------------------- def paging(request): all_data_list=[] each_page_num=10 all_num_control=100 for i in range(1,all_num_control): all_data_list.append(i) all_data_len=len(all_data_list) totally_num,mod=divmod(all_data_len,each_page_num) if mod!=0: totally_num+=1 choice_page=int(request.GET.get('p',1)) start_no=(choice_page-1)*each_page_num end_no=start_no+each_page_num tmp_data_list=all_data_list[start_no:end_no] html_str=" " for i in range(1,totally_num+1): if i == choice_page: html_str +="<a href='/paging?p=%s' class='page_num active'>%s</a> "%(i,i) else: html_str += "<a href='/paging?p=%s' class='page_num'>%s</a> " % (i, i) html_str=mark_safe(html_str) return render(request,'paging.html',{'record_list':tmp_data_list,'html_str':html_str})
3、阶段三 引入变量做成标准模块
3.1 需要考虑的变量
->列表数量变量
->每页显示数量
->一次显示多少页码
->当前页
->前一页、后一页
->首页、尾页
3.2 需要考虑的情况
->在页码充足的情况下的显示
->接近首页
->接近尾页
->总页数少于一页锁展示的页码
4、实例
class Page(object): def __init__(self,totally_page_num,choice_page,show_choice_page_no,path): #获取变量 获得三个变量 self.totally_page_num = totally_page_num self.choice_page = choice_page self.show_choice_page_no = show_choice_page_no self.path=path @property def page_export(self): html_str = " " if self.choice_page == 1: html_prev_button = "<a href='%s?p=%s'>上一页</a>" % (self.path,self.choice_page) html_next_button = "<a href='%s?p=%s'>下一页</a>" % (self.path,self.choice_page + 1) elif self.choice_page == self.totally_page_num: html_prev_button = "<a href='%s?p=%s'>上一页</a>" % (self.path,self.choice_page - 1) html_next_button = "<a href='%s?p=%s'>下一页</a>" % (self.path,self.choice_page) else: html_prev_button = "<a href='%s?p=%s'>上一页</a>" % (self.path,self.choice_page - 1) html_next_button = "<a href='%s?p=%s'>下一页</a>" % (self.path,self.choice_page + 1) html_str += html_prev_button start_page_no = self.choice_page - self.show_choice_page_no end_page_no = self.choice_page + self.show_choice_page_no if start_page_no < 1: start_page_no = 1 end_page_no = 1 + self.show_choice_page_no + self.show_choice_page_no if end_page_no > self.totally_page_num: end_page_no = self.totally_page_num start_page_no = self.totally_page_num - self.show_choice_page_no - self.show_choice_page_no for i in range(start_page_no, end_page_no + 1): if i == self.choice_page: html_str += "<a href='%s?p=%s' class='page_num active'>%s</a> " % (self.path,i, i) else: html_str += "<a href='%s?p=%s' class='page_num'>%s</a> " % (self.path,i, i) html_str += html_next_button html_str = mark_safe(html_str) return html_str def paging(request): #part 1 初始化数据 all_data_list=[] all_num_control = 1000 for i in range(1,all_num_control): all_data_list.append(i) all_data_len = len(all_data_list) show_choice_page_no = 8 #初始化数据结束 #获得 #1、数据长度 all_data_len #2、总数据列表 all_data_list #part 1结束 # part2 # 定义每页显示的数据量 #计算总共需要多少页 each_page_num=20 totally_page_num,mod=divmod(all_data_len,each_page_num) if mod!=0: totally_page_num+=1 #获得总共需要生成的页数 # part2 结束 #part3 #需要获得用户选定的页码 choice_page=int(request.GET.get('p',1)) #获取到客户选定的页码 #part 3结束 #part4 此时总页码数量,客户选定的页码数量,总共要显示的页码区间已经获得到了 #此时可以根据上述的3个变量来生成分页的 page_line_obj=Page(totally_page_num,choice_page,show_choice_page_no,'/paging') html_str=page_line_obj.page_export #part4 结束 html字符串生成 #为了方便扩展,此处添加一个新变量叫path变量,简单展示,不做扩展 #part5 生成当前页的数据 start_data_no=(choice_page-1)*each_page_num end_data_no=start_data_no+each_page_num tmp_data_list=all_data_list[start_data_no:end_data_no] #part 6 返回客户端界面 return render(request,'paging.html',{'record_list':tmp_data_list,'html_str':html_str})
十四、cookie
1、cookie的本质
cookie的本质就是服务器保存在浏览器的一些键值对,一些key对应一些value
2、cookie的最基本操作
2.1 设置cookie
t_res=render(request,'paging.html',) t_res.set_cookie('per_page_num',123) return t_res
2.2 获取cookie
request.COOKIES.get('k1') request.COOKIES['k1']
3、cookie简单实现用户登录
login data.set_cookie('user_auth', t_u) return data index name=request.COOKIES.get('user_auth') return HttpResponse('welcome %s'%name)
4、设置cookie时效的参数
4.1 设置cookie
data.set_cookie('k',v) #关闭浏览器即失效
4.2 获取cookies(两种方式)
request.COOKIES.get('k1') request.COOKIES['k1']
4.3 cookies的两种时效设置
#方法一 max_age 单位s data.set_cookie('user_auth',t_u,max_age=10) #方法二 expires 设置时间 outtime_date=datetime.datetime.utcnow()+datetime.timedelta(hours=1) t_return_data.set_cookie('user_auth', t_u, expires=outtime_date) return t_return_data
4.4 httponly、domain、path、secure
httponly:httponly只允许http协议获取cookie(document.cookie / $.cookie 通过js或jquery能获取cookie)
domain : 生效的域名
path : 哪些页面发送这个cookie
secure=True : https传输时候需要置为True
5、cookie在浏览器中的查看
6、cookie实现注销
思路 将max_age或expire设置为当前时间(datetime.datetime.utcnow())即可
注意:和session对比,session的注销可以通过session.clear()来实现,但是cookie不行
7、cookie实现分页定制的操作
7.1 jquery 与 jquerycookie的应用
jquery cookie是一个依赖于jquery的插件,需要单独下载引入
$.cookie('k1','v1',{"path":'/paging'}) #三个部分,key、value、字典 #字典内包括了cookie可以带的那些参数,max_age/expire/httponly等
7.2 select框添加onchange事件
设置cookie $.cookie('per_pagenum':$(this).val())
添加location.reload(),使得配置的页面立即生效
7.3 views函数返回
->获取当前页面 request.GET.get('p')
->获取cookies中每页显示的数目 request.COOKIES.get()
->分页函数处理
->已选择的每页显示的select显示指定的option
->$('select').val($.cookie('per_pagenum'))
7.4 完整代码
{% block js %} <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $('#per_page_num_select').val($.cookie('per_page_num')); $('#per_page_num_select').change( function () { $.cookie('per_page_num',$(this).val()); location.reload() } ) </script> {% endblock %} def paging(request): #part 1 初始化数据 val = request.COOKIES.get('per_page_num',30) print(val) all_data_list=[] all_num_control = 1000 for i in range(1,all_num_control): all_data_list.append(i) all_data_len = len(all_data_list) show_choice_page_no = 5 #初始化数据结束 #获得 #1、数据长度 all_data_len #2、总数据列表 all_data_list #part 1结束 # part2 # 定义每页显示的数据量 #计算总共需要多少页 each_page_num=int(val) totally_page_num,mod=divmod(all_data_len,each_page_num) if mod!=0: totally_page_num+=1 #获得总共需要生成的页数 # part2 结束 #part3 #需要获得用户选定的页码 choice_page=int(request.GET.get('p',1)) #获取到客户选定的页码 #part 3结束 #part4 此时总页码数量,客户选定的页码数量,总共要显示的页码区间已经获得到了 #此时可以根据上述的3个变量来生成分页的 page_line_obj=Page(totally_page_num,choice_page,show_choice_page_no,'/paging') html_str=page_line_obj.page_export #part4 结束 html字符串生成 #为了方便扩展,此处添加一个新变量叫path变量,简单展示,不做扩展 #part5 生成当前页的数据 start_data_no=(choice_page-1)*each_page_num end_data_no=start_data_no+each_page_num tmp_data_list=all_data_list[start_data_no:end_data_no] #part 6 返回客户端界面 t_res=render(request,'paging.html',{'record_list':tmp_data_list,'html_str':html_str}) t_res.set_cookie('per_page_num',str(val)) return t_res
8.、cookie的加密--salt(加盐)
t_res.set_signed_cookie('per_page_num',str(val),salt='xxxx') request.get_signed_cookie('per_page_num',salt='xxxx')
9、使用cookie实现认证 装饰器版
9.1 fbv 自定义一个装饰器即可
ef is_it_auth(fun): def inner(request,*args,**kwargs): t_u = request.COOKIES.get('user_auth') if t_u: t_result=fun(request) else: t_result=redirect('/lesson3/login') return t_result return inner @is_it_auth def l3_index(request): t_u = request.COOKIES.get('user_auth') return render(request,'l3_index.html',{'user_name':t_u,'user_detail':user_data[t_u]})
9.2 cbv的三种情况及需要使用的django装饰器
导入django装饰器 ->from django.utils.decorators import method_decorator
9.2.1 针对单个动作做认证,如get,自己定义的函数还要经过method_decorator处理
from django.views import View from django.utils.decorators import method_decorator class testcbv(View): @method_decorator(is_it_auth) def get(self,request): t_u = request.COOKIES.get('user_auth') return render(request, 'l3_index.html', {'user_name': t_u, 'user_detail': user_data[t_u]}) def post(self,request): pass
9.2.2 针对所有动作的操作
装饰dispatch函数
class testcbv(View): @method_decorator(is_it_auth) def dispatch(self, request, *args, **kwargs): t_return=super(testcbv,self).dispatch(request, *args, **kwargs)#继承所有的方法 return t_return
9.2.3 针对类的装饰器
@method_decorator(is_it_auth,dispatch) class TheOBJ(View)
十五、session
1、cookie的缺点及注意事项
不能包含敏感信息
2、session的本质
2.1 保存在服务器端的键值对(字典)
2.2 session与cookie的关系
2.3 session必须依赖于cookie
2.4 服务器给客户端一个cookie内部保存一个随机字符串
2.5 服务器端保存此随机字符串对应的相关信息
3、session完成用户登录
3.1 过程
3.1.1 生成随机字符串
3.1.2 将随机字符串返回给浏览器(通过cookie)
3.1.3 本地保存随机字符串(默认保存在数据库中)
3.1.4 给随机字符串管理相应的字典保存数据
3.2 操作
django中将上述过程全部封装,只需要执行一步指令即可
request.sesson['key']=value #设置值 x=request.session['key'] #取出值
4、用户登录认证实例
def deal_session_login(request): if request.method=='POST': t_u=request.POST.get('name') t_p=request.POST.get('passwd') t_man=user_data.get(t_u) print(t_u,t_p) if not t_man or t_man['pwd']!=t_p: return render(request,'l3_deal_session_login.html',{"err_msg":'用户名或密码错误'}) if t_man['pwd']==t_p: request.session['name']=t_u request.session['is_login']=True t_return_data=redirect('/lesson3/deal_session_index') return t_return_data return render(request,'l3_deal_session_login.html') def deal_session_index(request): print('---begin') xxx=request.session.get('is_login') print('----',xxx) if xxx: return HttpResponse('welcome,%s'%request.session['name']) else: return redirect('/lesson3/deal_session_login')
5、session的方法及参数
5.1 获取值
request.session.get('key',none)#获取值 request.session['key']#如果key不存在会报错
5.2 设置值
request.session.setdefault('key',value)#如果不存在,则设置,如果存在不改变 request.session['key']=123
5.3 删除值
del request.session['key']
5.4 对字典的操作可以运用在request.session上
request.session.keys() request.session.values() request.session.items()
5.5 获取当前用户的随机字符串
request.sesson.session_key
5.6 删除脏数据
request.session.clear_expired() #假如cookie被删除,或过了超时时间,则会申请新的随机字符串,那么此时数据库中将存在旧的字符串的脏数据,可以通过此命令主动删除
5.7 删除整个session
request.session.delete(request.session.session_key) request.session.clear()#和上面功能一样,但是不要去获取随机值
5.8 注销的实现
原理即通过request.session.clear()来清除session完成注销
5.9 注销实例代码
def deal_session_loginout(request): request.session.clear() return redirect('/lesson3/deal_session_login') <body> <h3>{{ user_name }}</h3> <h2>{{ user_detail.role }}</h2> </body>
5.10 session的超时时间
django默认的超时时间是两周,这里包括了数据库的保存时间以及cookie的保存时间。
注意:session依赖于cookie,如果浏览器清空了cookie,那么这个超时时间也就没有意义了
5.11 session修改超时时间
5.11.1 全局的超时时间
在配置文件中配置
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
5.11.2 单用户自己定义的超时时间
request.session.set_expiry(10)#单位是秒 #这个值可以前端定义返回给后端
5.12 session的配置文件setting中配置
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)#默认django中关闭浏览器不会清除cookies #如果这个设置为True 则本地的cookies被清空 数据库中的session也就失效了 cookies-age也就没有用了 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
5.13 session配置中的重要的用法---SESSION_SAVE_EVERY_REQUEST
实例:SESSION_SAVE_EVERY_REQUEST和cookie结合实现10s无操作则退出
setting中 SESSION_SAVE_EVERY_REQUEST=True views中 -------------------- request.session.set_expiry(10) ---------------------- t_timeout=request.POST.get('settimeout',0) if t_timeout: request.session.set_expiry(10) #不断刷新界面 则不退出,如果不刷新,则10s后超时
6、session可以保存的位置
session可以通过配置保存在不同的地方
->文件中
->数据库中
->cache缓存中
->加密cookie中
->数据库+缓存中(缓存没有命中,则保存到数据库)
十六、CSRF
1、csrf原理
可以看看这位老哥写的文章:https://www.cnblogs.com/phpstudy2015-6/p/6771239.html#_label2
总结就是一些涉及到钱的get或post动作,如果他人在第三方网站上嵌套了这类型的iframe,那么你带着你的cookie去访问,则你账户的钱就丢失了
简单的解决思路就是我给你一串随机字符串,你在发送交易动作的时候带上我给你的字符串,我知道是你主动访问的
2、form的csrf提交方式
{%csrf_token%}
form中自动生成一个input框,将csrf_token随着post动作发送给对端
3、ajax对csrf的处理方式(两种)
3.1 只对此ajax生效的方法->设置X-CSRFtoken头部
$.ajax( { url:'' method:'' data:{} headers:{'X-CSRFtoken':$.cookie('csrftoken')} success:function(data){} } )
3.2 对于所有ajax请求批量生效的方法->xhr.setRequestHeader
#AJAX 实例 XHR 请求 XMLHttpRequest 是 AJAX 的基础 $.ajaxSetup( { beforeSend:function (xhr,settings) { xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')); } } );
4、全局csrf与局部csrf
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
● @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
● @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
十七、中间件
1、中间件的本质
中间件的本质就是一个类,注册后的中间件,当请求来时会调用类中的方法在全局情况下对所有的请求和回复进行处理
2、中间件中的5个方法
->process_request(self,request) ->process_view(self,request,callback,callback_args,callback_kwargs) ->process_response(self,request,response) ->process_exception(select,request,exception) ->process_template_response
3、定义中间件
3.1 定义类
from django.utils.deprecation import MiddlewareMixin
class M1(MiddlewareMixin)
3.2 注册中间件
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
4、中间件中方法的理解
4.1 阶段一 理解process_request(self,request)与process_response(self,request,response)
process_request(self,request)
->请求到达服务端,request先过此方法
->按中间件的注册顺序执行,即离views函数最远的先执行
->如果无操作(无return),直接到达下一个中间件的process_request
->如果有返回值
->1.10以前版本给最靠近views的process_response做处理,依次返回
->1.10之后版本交给自己的process_response处理后依次返回,不会到最贴近views的那个response了
process_response(self,request,response)
->views函数处理完请求后,返回值会依次经过离views函数最近->离views函数最远
->此方法需要return参数内的response或者自己定义的response
from django.utils.deprecation import MiddlewareMixin class M1(MiddlewareMixin): def process_request(self,request): print('m1-request') def process_response(self,request,response): print('m1-response') return response class M2(MiddlewareMixin): def process_request(self,request): print('m2-request') def process_response(self,request,response): print('m2-response') return response class M3(MiddlewareMixin): def process_request(self,request): print('m3-request') def process_response(self,request,response): print('m3-response') return response
4.2 基于process_request实现黑名单阻塞
class BlockedIpMiddleware(object): def process_request(self, request): if request.META['REMOTE_ADDR'] in getattr(settings, "BLOCKED_IPS", []): return http.HttpResponseForbidden('<h1>Forbidden</h1>')
4.3 阶段二 理解process_view(self,request,callback,callback_args,callback_kwargs)
->此阶段在画图的时候通常把它画在urls路由系统之前
->但是从参数可以看出实际上在process_request的基础上,request.path_info内所带的一些参数已经被处理过
->此方法和process_request一样,不需要有返回值return
->如果有返回值,则讲给自己的response之后按response的顺序返回给客户端(与process_request返回值的现象是一致,包括版本)
->此方法的执行顺序是按注册顺序执行,即离views函数最远的先执行
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class M1(MiddlewareMixin): def process_request(self,request): print('m1-request') def process_response(self,request,response): print('m1-response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print('m1_view') class M2(MiddlewareMixin): def process_request(self,request): print('m2-request') def process_response(self,request,response): print('m2-response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print('m2_view') class M3(MiddlewareMixin): def process_request(self,request): print('m3-request') def process_view(self, request, callback, callback_args, callback_kwargs): print('m3_view') def process_response(self,request,response): print('m3-response') return response
4.4 阶段三 理解process_exception(select,request,exception)
->此方法为处理异常的方法。如果没有异常,则不处理
->如果有异常,按离views最近->最远的顺序一次执行
->如果没有返回值,则执行下一个process_exception
->如果有返回值,直接交给离views最近的process_response处理(这个和process_request、process_view不同)
4.5 阶段四 理解process_template_response
->运用场景少
->process_template_response方法的执行取决于视图函数的返回的信息
->视图函数如果使用render方法返回信息,中间件里的process_template_response方法就会被执行.
4.6 概况有中间件的情况下的整个生命周期
请求->process_requess->process_views->urls路由->views模板渲染,数据库操作->process_template_response->process_exception->process_response
4.7 一个老版本的生命周期图
十八、缓存
1、保存的位置
开发调试(实际就是什么地方都不放)
内存
文件
数据库
Memcache缓存(python-memcached模块)
Memcache缓存(pylibmc模块) 两个可以看做一个
2、本质
将渲染好的html本地保存一份,之后浏览器发来的请求就直接回复保存的内容
3、setting中配置
缓存保存在文件中的配置 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': os.path.join(BASE_DIR,'local_cache'), } }
4、如何使用缓存(3种类型)
4.1 单独视图函数加装饰器,整个界面做缓存
-> from django.views.decorators.cache import cache_page @cache_page(10) 单位是秒
4.2 对页面的某个部分做缓存
{%load cache%} #和自定义的simpletag有点像 {%cache 5000 k1%} 5000即秒,k1为缓存的名称 {%endcache%}
实例
{% load cache %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{t_time}}<hr /> {{t_time}}<hr /> {% cache 5 t_time %} {{t_time}}<hr /> {% endcache %} </body> </html>
4.3 对所有页面做缓存
思路:中间件处理
django为我们准备好了两个中间件
4.3.1 添加中间件的位置及两个中间件的原理(为什么要有两个)
->一个用来获取最终的request请求,并和库匹配,看看是否有缓存。如果存在,则直接返回缓存
->一个用来在离用户最近的位置判断,是否存在缓存,如果不存在,则写入、
MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ]
5、多种缓存存在的时候,哪个生效
按照请求周期来分析,如果先return了 那么不会到达后面的函数
十九、信号
1、信号是什么
相当于django内部给我们预留了钩子进行操作
2、内置信号(django为我们预留的钩子)
Model signals pre_init # django的modal执行其构造方法前,自动触发 post_init # django的modal执行其构造方法后,自动触发 pre_save # django的modal对象保存前,自动触发 post_save # django的modal对象保存后,自动触发 pre_delete # django的modal对象删除前,自动触发 post_delete # django的modal对象删除后,自动触发 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发 Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 Test signals setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 Database Wrappers connection_created # 创建数据库连接时,自动触发
3、内置信号的使用方法
3.1 导入
from django.core.signals import request_finished from django.core.signals import request_started from django.core.signals import got_request_exception from django.db.models.signals import class_prepared from django.db.models.signals import pre_init, post_init from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_delete, post_delete from django.db.models.signals import m2m_changed from django.db.models.signals import pre_migrate, post_migrate from django.test.signals import setting_changed from django.test.signals import template_rendered from django.db.backends.signals import connection_created
3.2 注册
3.3 __init__中使用
4、自定义信号
->定义信号
->注册信号
->触发信号
#定义 import django.dispatch access_web = django.dispatch.Signal(providing_args=["ip"]) #注册 def record_access_web(*args,**kwargs): print('99999999999999999999999') print(args) print('00000000000000000000') print(kwargs) print('xxx') #触发 access_web.connect(record_access_web)
二十、form入门
1、form的概述及与model的关系
1.1 form就是一个form,用来做数据验证和生产html标签
1.2 model用来提供数据库表结构的定义,及表内容的增删改查,及少量的数据验证功能
1.3 form和model是两个独立的部分,在django中通常被结合在一起使用,完成数据库相关操作及数据验证操作。
2、定义一个form的方法
2.1 思路
2.1.1 定义一个类继承forms.Form
2.1.2 定义字段
2.1.3 设置字段内的参数,完成数据验证及html生成的相关配置(此处先不深究这些参数,先生成一个form再说)
from django.forms import fields,forms class MYForm(forms.Form): name=fields.CharField(max_length=32,min_length=8) pwd=fields.CharField() email=fields.EmailField(error_messages={'invalid':'格式错误'})
3、数据验证
3.1 web界面发送数据POST
3.2 form接收数据并验证数据
obj.is_valid() 返回True或False判断是否符合格式
def fm(request): if request.method=="GET": return render(request,'fm.html') if request.method=="POST": obj=FM_AUTH(request.POST) t_result=obj.is_valid() return HttpResponse(t_result)
#注意此时form中的字段和input标签的name要一样,
4、查看错误信息的方法
4.1 obj.errors内保存了用户所有的错误信息,信息保存的形式
{ ‘name’:[ValidationError] #'name':[{'code':xxx,'message':'errormsg'}] 可以理解为这种形式 ‘pwd’:[ValidationError] # ‘email’:[ValidationError] # }
4.2 批量输出错误信息的几种方式
->obj.errors 显示为ul li格式 <ul class="errorlist"> <li> name<ul class="errorlist"><li>Ensure this value has at least 8 characters (it has 4).</li></ul> </li> <li> email<ul class="errorlist"><li>Enter a valid email address.</li></ul> </li> </ul> ->obj.errors.as_json() 能显示错误代码code { "name": [{ "message": "Ensure this value has at least 8 characters (it has 4).", "code": "min_length" }], "email":[{ "message": "Enter a valid email address.", "code": "invalid" }] } ->obj.errors.as_p() 此处不展开 ->obj.errors.as_table()此处不展开 as_table 需要写在<table></table>中
5、定制错误信息的显示
默认显示的错误信息,我们可以修改
需要指定相应的code
定制方法是使用字段内的参数error_messages
pwd=forms.CharField(max_length=16,min_length=6,error_messages={'min_length':'太短','max_length':'太长'}) #error_messages内部字典的key就是as_json()时候的code
6、获取某个字段错误的方法
obj.errors['key'] --><ul class="errorlist"><li>Ensure this value has at least 8 characters (it has 4).</li></ul> obj.errors['key'][0] -->Ensure this value has at least 8 characters (it has 4). #模板语言
obj.errors.key.0
#
for i in obj:
i.errors.0
7、验证成功后,获取数据的方式
cleaned_data
#验证成功后,数据被保存在cleaned_data中 是一个字典的格式 #cleaned_data可以直接被插入数据库 models.xxx.objects.create(**cleaned_data)
8、实例:自动生成input框并输出错误提示,且保留原值
#自动生成input框,输出错误信息(仅文字) {{obj.user}}{{obj.user.errors.0}} {{obj.pwd}}{{obj.pwd.errors.0}} {{obj.email}}{{obj.email.errors.0}} #保留原值 x=MYFORM(request.POST)
9、修改form默认生成的标签类型及样式
form本身只有数据验证功能,生成html的功能是由widget插件来完成的
9.1 通过widget插件参数,及attrs参数
9.2 charFields是继承Field,Field中定义了widget=TextInput
->class CharField(Field): def __init__(self, *, max_length=None, min_length=None, strip=True, empty_value='', **kwargs): ->class Field: widget = TextInput # Default widget to use when rendering this type of Field. ->class TextInput(Input): input_type = 'text' template_name = 'django/forms/widgets/text.html' -><input type="{{ widget.type }}" name="{{ widget.name }}" {% if widget.value != None %} value="{{ widget.value }}" {% endif %} {% include "django/forms/widgets/attrs.html" %}>
9.3 修改默认的插件
#给字段设置插件 widget=widgets.* from django.forms import widgets email=fields.EmailField(error_messages={'invalid':'格式错误'},widget=widgets.Textarea(attrs={'class':'c1','xxx':'xxx2'})) #设置样式 attrs={'class':c1,'xxx':'xxx2'} #再来个例子 pwd=fields.CharField(max_length=16,min_length=6,error_messages={'min_length':'太短','max_length':'太长'},widget=widgets.PasswordInput())#不写括号也可以
10、form的字段类型及参数
Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否可以编辑 label_suffix=None Label内容后缀 CharField(Field) max_length=None, 最大长度 min_length=None, 最小长度 strip=True 是否移除用户输入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否允许空文件 ImageField(FileField) ... 注:需要PIL模块,pip3 install Pillow 以上两个字典使用时,需要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每一个值进行一次转换 empty_value= '' 空值的默认值 ComboField(Field) fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型
11、form的常用参数
required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 ->obj.user.label在生成html时,给input框等标签之前添加说明 initial=None, 初始值
->initial={'user':123...} help_text='', 帮助信息(在标签旁边显示) ->在后面的项目中有实例 error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) validators=[], 自定义验证规则,可使用正则表达式
disabled=False, 是否可以编辑
12、自定义认证规则的实例
12.1 实例1
username = fields.CharField( max_length=5, validators=[ RegexValidator( r'^[0-9]+$', 'Enter a valid extension.', 'invalid') ], )
12.2 实例2
def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号码格式错误') class PublishForm(Form): # 使用自定义验证规则 phone = fields.CharField( validators=[mobile_validate, ], error_messages={'required': '手机不能为空'}, widget=widgets.TextInput(attrs={'class': "form-control",'placeholder': u'手机号码'}) )
13、对于那么多字段类型的理解
我们主要还是使用CharField来进行定义
我们可以通过自定义正则的方式,来自行定义出很多django内部带的或者是没有带的数据类型
14、文件处理 FileField
参数allow_empty_file=False 是否允许空文件
14.1 正常使用常规的文件处理
def upload(request): if request.method=="GET": return render(request,'upload.html') if request.method=="POST": tmp_file_obj=request.FILES.get('uploadfile') if tmp_file_obj: f=open(tmp_file_obj.name,'wb') for i in tmp_file_obj.chunks(): f.write(i) f.close() return HttpResponse('ok') else: return HttpResponse('err') <body> <form action="/lesson3/upload" method="post" enctype="multipart/form-data"> <input type="file" name="uploadfile"> <input type="submit" value="upload"> </form> </body>
14.2 使用field.FileField()来处理文件
class UploadFile(forms.Form): uploadfile=fields.FileField(allow_empty_file=False) def upload_plus(request): if request.method=="GET": obj=UploadFile() return render(request,'upload_plus.html',{'obj':obj}) if request.method=="POST": obj=UploadFile(request.POST) if obj.is_valid(): fileobj=obj.cleaned_data['uploadfile'] f=open(fileobj.name,'wb') for i in fileobj.chunks(): f.write(i) f.close() return HttpResponse('ok') else: return HttpResponse('err') <body> <div>plus</div> <form action="/lesson3/upload" method="post" enctype="multipart/form-data"> {{ obj.uploadfile }} <input type="submit" value="上传"> </form> </body>
15、选项标签 radio、checkbox、select的生成方法
15.1 单选下拉框
#使用choicesField ->choicesField(choices=[(1,上海),(2,广州),(3,北京)],widget=widgets.Select) #使用CharField ->user = fields.CharField(initial=2,widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)))
15.2 多选的下拉框
MultipleChoiceField(choices=((1,上海),(2,广州),(3,北京))#列表/元组都可以
15.3 单选的radio处理
#使用charField user = fields.CharField( initial=2, widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) ) #使用ChoiceField user = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.RadioSelect )
15.4 单选的checkbox处理
user = fields.CharField(widget=widgets.CheckboxInput())
15.5 多选checkbox,值为列表
user = fields.MultipleChoiceField( initial=[2, ], choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxSelectMultiple )
16、选项标签的实例
from django.forms import widgets class Man(forms.Form): name=fields.CharField(max_length=12,min_length=1,label='name') pwd=fields.CharField(max_length=12,min_length=6,label='密码',widget=widgets.PasswordInput(attrs={'class':'c1','xxx':'999'})) email=fields.EmailField() sex=fields.ChoiceField( choices=((1,'男'),(2,'女')), widget=widgets.RadioSelect, label='sex', ) city=fields.ChoiceField( choices=((1,'北'),(2,'上'),(3,'广'),(4,'深')), widget=widgets.Select, label='city' ) hobit=fields.MultipleChoiceField( choices=((1, '吃'), (2, '喝'), (3, '睡觉'), (4, '打酱油'),), widget=widgets.CheckboxSelectMultiple, label='hobit', ) agree=fields.ChoiceField( widget=widgets.CheckboxInput, label='我同意此协议', ) def select(request): if request.method=="GET": obj=Man() return render(request,'select.html',{'obj':obj}) if request.method=="POST": obj=Man(request.POST) obj.is_valid() x=obj.cleaned_data print(x) return HttpResponse('ok!') <body> <form method="post"> <div>{{ obj.name.label }}{{ obj.name }}</div> <div>{{ obj.pwd.label }}{{ obj.pwd }}</div> <div>{{ obj.email.label }}{{ obj.email }}</div> <div>{{ obj.sex.label }}{{ obj.sex }}</div> <div>{{ obj.hobit.label }}{{ obj.hobit }}</div> <div>{{ obj.agree.label }}{{ obj.agree }}</div> <input type="submit" value="submit"> </form> </body>
17 form的初始化操作
initial初始化字段
username = fields.CharField(max_length=32,initial=123)
使用字典初始话form界面生成的html则带有内容
class MYForm(forms.Form): username = fields.CharField(max_length=32,initial=123) email = fields.EmailField() choices=[] usertype_id = fields.ChoiceField(label='用户类型',choices=choices) def __init__(self,*args,**kwargs): x=super(MYForm, self).__init__(*args,**kwargs) self.fields['usertype_id'].choices = models.UserType.objects.all().values_list() return x def login(request): t_data=models.UserInfo.objects.filter(id=2).first() t_data_dict=t_data.__dict__ t_dict={} obj=MYForm(t_data_dict) return render(request,'123.html',{'obj':obj})
附录:提纲一
Html补充 Input 的 placeholder 与 a标签的 text-decoration: none; 与 style="cursor: pointer" django基本操作 ->简介 ->django版本 ->创建project ->方式一 通过django-admin.exe创建 ->django-admin.exe startproject projectnamexxx ->python manage.py runserver 测试 ->方式二 通过pycharm来创建 ->new django project(图形化) ->packaging_tool.py 修改方式 ->django的目录结构 ->manage.py ->python manage.py runserver 127.0.0.1:8888 ->python manage.py startapp xxx ->python manage.py makemigrations ->python manage.py migrate ->db.sqllite3 ->setting.py ->urls.py ->wsgi.py ->__init__.py ->实例:添加一个url,返回HttpResponse ->建立app ->app的概念 ->创建app ->manag.py startapp xxx ->功能下发到views中 ->url的修改 ->app的目录结构 ->migrations ->admin ->apps ->models ->test ->views ->实例一:访问web打开一个web界面 ->方法一:Httpresponse ->方法二:render ->需要配置DIRS ->静态请求的配置(css js jquery文件的位置) ->实例二:验证密码,正确跳转,错误界面提示 ->redirect ->模板语言 ->django请求的生命周期 ->收到请求 ->路由系统 ->视图函数 ->界面模板 ->界面渲染(模板语言、数据库models操作) ->返回用户 ->获取内容 ->method ->get.get/post.get ->get.getlist/post.getlist/post ->上传文件 Files.get ->name ->chunks ->fbv与cbv —>模板语言 ->变量 ->列表的下标 ->字典的下标 ->循环 ->循环列表 ->循环字典 ->条件 ->实例 查看详情 ->re_path正则表达式的使用 ->(\d+) (\w+) ->分组 (?P<nid>\d+) ->views中接收参数的方式 ->*args/**kwargs(多参数情况) ->顺序 ->变量名(分组的情况下)
->templates中生成url的方法 ->reverse 生成url的方法 ->request.path_info
->urls
->多变量情况 ->urls 路由分发 include ->django对数据库的操作 ->连接mysql ->连接mysql的基本设置 ->app注册 ->定义单表 ->生成表 ->makemigrations/migrate ->migrations文件夹下操作记录的删除 ->ORM概念 ->单表的增删改查 ->增 ->3种新增记录的方法 ->models.xxx.obj.create(k1=xxx,k2=xxx) ->models.xxx.obj.create(**xxx_dict) ->tmp_obj=models.xxx(k1=xxx,k2=xxx) tmp_obj.save() ->查 ->全查 ->过滤 ->单个过滤条件 ->多个过滤条件 ->values ->vlaues_list ->get ->容易出错 ->删除 ->delete ->改 ->update ->实例:用户信息管理 ->数据表结构的修改 ->增加列 ->给默认值 ->留空 ->删除列 ->直接删除 ->修改 ->直接修改 ->新建表 ->默认的自增id ->表的数据类型及字段参数 ->数据类型 ->django admin的使用(账号的建立) ->整数/长整数/浮点数 ->二进制数 ->字符型 ->日期型 ->自增 ->django自有的email、ip类型等 ->参数类型 ->null ->default ->primary key ->db_column ->db_index ->unique ->几种索引的理解 ->auto_now ->auto_now_add ->注意时间类型及修改时间生效的方式 ->choice ->blank ->editable ->verbose_name -> django admin显示字段中文 ->error_messages -> 错误信息欠 ->help_text -> django admin提示 ->validators -> django form ,自定义错误信息(欠) ->一对多操作 ->如何建立外键
->外键字段再数据库中的保存形式_id
->一对多的情况下如何添加数据
->一对多的情况下如何获取数据
_id
多对多操作 ajax
补充url的命名空间
附录:提纲二
->request对象所包含的信息 不难发现,我们不论获取什么信息如POST内容、GET内容、PATH路径等都是从request中获取,现有疑问,数据包交互的过程中,包头及包内还有很多其他的内容,我们如何去获取呢,能从request中去获取吗 ->request.environ 获取数据包内其他的信息 ->django不会去处理request对象中所有的信息,它只帮我们处理了一些较常用的POST、GET动作等,如果涉及到其他关于数据包的操作,如获取请求的UA时,我们就需要去request.environ中自己去获取了。 ->request.environ是一个字典,我们通过for的方式查看内部所有的值,此处不展开了 ->模板处理 ->{%extends 'test.html'%}与block ->普通内容content ->{% block content %} ->js与css的处理 ->{% block css %} ->{% block js %} ->include ->一个html文件只能引用一个模板即一个extends,不能引用多个 ->对于一些很多页面都需要使用的小组件,我们可以将其单独写成一个html文件并使用include来调用 ->{% include 'tag.html' %}来使用 {% extends 'xxx_base.html' %} ->例子 ->base <body> base {% block content %} {% endblock %} </body> ->main {% block content %} <div>it is content</div> {% for i in t_dic %} {% include 'xxx_include.html' %} {% endfor %} {% endblock %} ->include <div>打开图片www.{{ i }}.com</div> ->效果 base it is content 打开图片www.1.com 打开图片www.2.com 打开图片www.3.com 打开图片www.4.com 打开图片www.5.com ->自定义tag与filter 相当于通过某种方式将前端web界面内容返回到python中,使用python做处理后再返回给前端 ->@register.simple_tag的使用 {%mysimpleTag k1 k2%} ->思路 a. app下创建templatetags目录 b. 创建任意.py文件 c. 创建template对象 d. 装饰器装饰函数 @register.simple_tag def func(a1,a2,a3....) return "asdfasd" e. settings中注册APP f. 顶部 {% load xxoo %} g. {% 函数名 arg1 arg2 %} h. 特殊情况 TEMPLATES = [ { 'DIRS':[os.path.join(BASE_DIR, 'templates'),], 'libraries':{'local_simpletags': 'table_admin.templatetags.local_simpletags',} }, ] 缺点: 不能作为if条件 优点: 参数任意 ->应用 ->直接输出返回结果 ->赋值给一个变量 {% deal_str all_dict.o as x %} {% if i == x %} ->filter a. app下创建templatetags目录 b. 任意xxoo.py文件 c. 创建template对象 register d. @register.filter def func(a1,a2) return "asdfasd" e. settings中注册APP f. 顶部 {% load xxoo %} g. {{ 参数1|函数名:"参数二,参数三" }} {{ 参数1|函数名:数字 }} 缺点: 最多两个参数,不能加空格 优点: 能作为if条件 ->分页功能的实现 ->阶段一 原理 ->列表切片 ->x[0,10] ->x[11,20] ->变量引入 ->x[y-1,y*10] ->将y这个变量赋值定义为get内的一个参数 ->xxx.com/?y=1 ->阶段二 基本实现 ->divmod的使用 -> >>> divmod(10,2) (5, 0) >>> divmod(10,6) (1, 4) ->a标签生成连接 -> "<a herf='/?y=%s'>%s</a>"%(y,y) -> 总页码数=divmod(k1,k2)[0] 或 divmod(k1,k2)[0]+1 -> for i in range 生成 ->xss攻击的处理 ->{{ html_str|safe }} -> from django.utils.safestring import mark_safe tmpstr=make_safr(tmpstr) ->阶段三 引入变量做成标准模块 ->需要考虑的变量 ->列表数量变量 ->每页显示数量 ->一次显示多少页码 ->当前页 ->前一页、后一页 ->首页、尾页 ->需要考虑的情况 ->在页码充足的情况下的显示 ->接近首页 ->接近尾页 ->总页数少于一页锁展示的页码 ->实例 ->cookies ->cookie是什么 本质 ->cookie简单实现用户登录 ->cookie的参数 ->设置cookie ->set_cookie('k',v) #关闭浏览器即失效 ->获取cookies ->request.COOKIES.get('k1') ->request.COOKIES['k1'] ->cookies的两种时间设置 ->data.set_cookie('user_auth',t_u,max_age=10) ->outtime_date=datetime.datetime.utcnow()+datetime.timedelta(hours=1) t_return_data.set_cookie('user_auth', t_u, expires=outtime_date) return t_return_data ->cookies的一些参数 主要还是时间有关的参数 ->max_age ->expires ->httponly ->document.cookie / $.cookie 通过js或jquery能获取cookie ->httponly只允许http协议获取cookie ->domain 生效的域名 ->path 是否是所有页面都发送这个cookie ->secure=False https传输 ->cookie在浏览器中的查看(有道笔记图) ->cookie实现注销 ->思路 将max_age或expire设置为当前时间即可 ->cookie实现分页定制的操作 ->jquery 与 jquery cookie的应用 ->$.cookie('k1','v1',{"path":'/paging'}) ->三个部分,key、value、字典 ->字典内包括了cookie可以带的那些参数,max_age/expire/httponly等 ->select框添加onchange事件 ->设置cookie $.cookie('per_pagenum':$(this).val()) ->添加location.reload()设置完成后立即生效 ->views函数的处理 ->获取当前页面 request.GET.get('p') ->获取cookies中没有显示的数目 request.COOKIES.get() ->分页函数处理 ->已选择的每页显示的select显示指定的option ->$('select').val($.cookie('per_pagenum')) ->完整代码 ->cookie的加密salt ->t_res.set_signed_cookie('per_page_num',str(val),salt='xxxx') ->request.get_signed_cookie('per_page_num',salt='xxxx') ->使用cookie实现认证 装饰器版 ->fbv 自定义一个装饰器即可 ->cbv的三种情况及需要使用的django装饰器 ->导入django装饰器 ->from django.utils.decorators import method_decorator ->针对单独动作的装饰,如get -> @method_decorator(is_it_auth) def get(self,request): ->针对所有动作的操作 ->装饰dispatch函数 ->写一个dispatch ->return super(THEOBJ,self).dispatch(request,*args,**kwargs) -> @method_decorator(is_it_auth) def dispatch(self,request): ->针对类的装饰器 @@method_decorator(is_it_auth,dispatch) class TheOBJ(View) ->request的补充 ->request.Meta ->请求头 ->request.body ->请求内容 ->request.environ ->处理后的一个请求的大字典 ->set_cookie是放置在请求头中的 ->响应头添加自定义字段的方法 ->response['k1']=v1 ->model中的反向查找的方法 ->关联表名b_set ->session ->cookie的缺点及注意事项 ->不能包含敏感信息 ->session的本质 ->保存在服务器端的键值对(字典) ->session与cookie的关系 ->session必须依赖于cookie ->服务器给客户端一个cookie内部保存一个随机字符串 ->服务器端保存此随机字符串对应的相关信息 ->session完成用户登录 ->过程 ->生成随机字符串 ->将随机字符串返回给浏览器(通过cookie) ->本地保存随机字符串(默认保存在数据库中) ->给随机字符串管理相应的字典保存数据 ->操作 ->django中将上述过程全部封装,只需要执行一步指令即可 ->request.sesson['key']=value 设置值 ->x=request.session['key'] 取出值 ->用户登录认证实例 ->有道笔记代码 ->session的方法及参数 ->获取值 ->request.session.get('key',none)获取值,此方法request.session['key']如果key不存在会报错 ->设置值 ->request.session.setdefault('key',value)如果不存在,则设置,如果存在不改变 ->request.session['key'] ->删除值 ->del request.session['key'] ->对字典的操作可以运用在request.session上 ->request.session.keys() ->request.session.values() ->request.session.items() ->获取当前用户的随机字符串 ->request.sesson.session_key ->删除脏数据 ->request.session.clear_expired() ->假如cookie被删除,或过了超时时间,则会申请新的随机字符串,那么此时数据库中将存在旧的字符串的脏数据,可以通过此命令主动删除 ->删除整个session ->request.session.delete(request.session.session_key) ->request.session.clear()和上面功能一样,但是不要去获取随机值 ->注销使用 ->注销实例代码 -> def deal_session_loginout(request): request.session.clear() return redirect('/lesson3/deal_session_login') ->图片 有道云 补充 ->session的超时时间 ->django默认的超时时间是两周 ->数据库保存时间 ->cookie的有效时间 ->需要考虑的情况 ->全局的超时时间 ->在配置文件中配置 ->单用户自己定义的超时时间 ->界面设置checkbox或select,后端读取后 ->request.session.set_expiry(10)单位是秒 ->session的配置文件setting中配置 ->SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)#默认django中关闭浏览器不会清除cookies 如果这个设置为True 则本地的cookies被清空 数据库中的session也就失效了 cookies-age也就没有用了 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) ->重要的用法是SESSION_SAVE_EVERY_REQUEST ->SESSION_SAVE_EVERY_REQUEST和cookie结合实现10s无操作则退出 ->SESSION_SAVE_EVERY_REQUEST=True ->request.session.set_expiry(10) ->t_timeout=request.POST.get('settimeout',0) if t_timeout: request.session.set_expiry(10) ->一张实例截图 ->session可以保存的位置 ->session可以通过配置保存在不同的地方 ->文件中 ->数据库中 ->cache缓存中 ->加密cookie中 ->数据库+缓存中(缓存没有命中,则保存到数据库) ->CSRF ->csrf原理 ->https://www.cnblogs.com/phpstudy2015-6/p/6771239.html#_label2 总结就是一些涉及到钱的get或post动作,如果他人在第三方网站上嵌套了这类型的iframe,那么你带着你的cookie去访问,则你账户的钱就丢失了 ->简单的解决思路就是我给你一串随机字符串,你在发送交易动作的时候带上我给你的字符串,我知道是你主动访问的 ->form的csrf提交方式 ->{%csrf_token%} ->ajax的处理方式(两种) ->只读此ajax生效 $.ajax( { url:'' method:'' data:{} headers:{'X-CSRFtoken':$.cookie('csrftoken')} success:function(data){} } ) ->批量生效 AJAX 实例 XHR 请求 XMLHttpRequest 是 AJAX 的基础 $.ajaxSetup( { beforeSend:function (xhr,settings) { xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')); } } ); ->中间件 中间件的本质就是一个类,注册后的中间件,当请求来时会调用类中的方法在全局情况下对所有的请求和回复进行处理 ->中间件中的5个方法 ->process_request(self,request) ->process_view(self,request,callback,callback_args,callback_kwargs) ->process_response(self,request,response) ->process_exception(select,request,exception) ->process_template_response ->定义中间件的方法 ->from django.utils.deprecation import MiddlewareMixin class M1(MiddlewareMixin) ->中间件的注册 ->MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ->中间件中方法的理解 ->阶段一 理解process_request(self,request)与process_response(self,request,response) process_request(self,request) ->请求到达服务端,request先过此方法 ->按中间件的注册顺序执行,即离views函数最远的先执行 ->如果无操作,直接到达下一个中间件的process_request ->如果有返回值 ->1.10以前版本给最靠近views的process_response做处理,依次返回 ->1.10之后版本交给自己的process_response处理后依次返回,不会到最贴近views的那个response了 process_response(self,request,response) ->views函数处理完请求后,返回值会依次经过离views函数最近->离views函数最远 ->此方法需要return参数内的response或者自己定义的response ->有道云图片 ->基于process_request实现黑名单阻塞 class BlockedIpMiddleware(object): def process_request(self, request): if request.META['REMOTE_ADDR'] in getattr(settings, "BLOCKED_IPS", []): return http.HttpResponseForbidden('<h1>Forbidden</h1>') ->阶段二 理解process_view(self,request,callback,callback_args,callback_kwargs) ->此阶段在画图的时候通常把它画在urls路由系统之前 ->但是从参数可以看出实际上在process_request的基础上,request.path_info内所带的一些参数已经被处理过 ->此方法和process_request一样,不需要有返回值return ->如果有返回值,则讲给自己的response之后按response的顺序返回给客户端(与process_request返回值的现象是一致,包括版本) ->此方法的执行顺序是按注册顺序执行,即离views函数最远的先执行 ->阶段三 理解process_exception(select,request,exception) ->此方法为处理异常的方法。如果没有异常,则不处理 ->如果有异常,按离views最近->最远的顺序一次执行 ->如果没有返回值,则执行下一个process_exception ->如果有返回值,直接交给离views最近的process_response处理(这个和process_request、process_view不同) ->有道 图 ->阶段四 理解process_template_response ->运用场景少 ->process_template_response方法的执行取决于视图函数的返回的信息 ->视图函数如果使用render方法返回信息,中间件里的process_template_response方法就会被执行. ->概况有中间件的情况下的整个生命周期 ->图 ->缓存 ->保存的位置 ->本质 将渲染好的html本地保存一份,之后浏览器发来的请求就直接回复保存的内容 ->setting中配置 ->文件版本的配置 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': os.path.join(BASE_DIR,'local_cache'), } } ->如何使用(3种类型) ->单独视图函数加装饰器,整个界面做缓存 -> from django.views.decorators.cache import cache_page @cache_page(10) 单位是秒 ->查看文件夹中cache截图 ->对页面的某个部分做缓存 ->{%load cache%} ->{%cache 5000 k1%} 5000即秒,k1为缓存的名称 {%endcache%} ->有道图片代码 ->对所有页面做缓存 ->中间件处理 ->添加中间件的位置及两个中间件的原理(为什么要有两个) ->一个取值 取response ->一个写值 写入response ->MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ] ->多种缓存存在的时候,哪个生效 ->按照请求周期来分析,如果先return了 那么不会到达后面的函数 ->信号 ->内置信号 ->相当于django内部给我们预留了钩子进行操作 ->内置信号的种类 Model signals pre_init # django的modal执行其构造方法前,自动触发 post_init # django的modal执行其构造方法后,自动触发 pre_save # django的modal对象保存前,自动触发 post_save # django的modal对象保存后,自动触发 pre_delete # django的modal对象删除前,自动触发 post_delete # django的modal对象删除后,自动触发 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发 Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 Test signals setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 Database Wrappers connection_created # 创建数据库连接时,自动触发 ->内置信号的使用方法 ->导入 from django.core.signals import request_finished from django.core.signals import request_started from django.core.signals import got_request_exception from django.db.models.signals import class_prepared from django.db.models.signals import pre_init, post_init from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_delete, post_delete from django.db.models.signals import m2m_changed from django.db.models.signals import pre_migrate, post_migrate from django.test.signals import setting_changed from django.test.signals import template_rendered from django.db.backends.signals import connection_created ->注册 ->__init__中使用 ->自定义信号 ->定义信号 ->注册信号 ->触发信号 ->有道图片 ->form入门 ->form的概述及与model的关系 ->django提供的验证模块,通常和model一起使用 ->form可以离开model单独对某个input框的内容做验证 ->定义一个form from django.forms import fields,forms class MYForm(forms.Form): name=fields.CharField(max_length=32,min_length=8) pwd=fields.CharField() email=fields.EmailField(error_messages={'invalid':'格式错误'}) ->is_valid()数据验证 ->数据传入 x=MYFORM(request.POST) ->通过返回true ->失败放回false ->errors ->obj.errors用来保存所有的错误信息 ->查看错误信息的几种方式 ->obj.errors 显示为ul li格式 <ul class="errorlist"> <li> name<ul class="errorlist"><li>Ensure this value has at least 8 characters (it has 4).</li></ul> </li> <li> email<ul class="errorlist"><li>Enter a valid email address.</li></ul> </li> </ul> ->obj.errors.as_json() 能显示错误代码code { "name": [{ "message": "Ensure this value has at least 8 characters (it has 4).", "code": "min_length" }], "email":[{ "message": "Enter a valid email address.", "code": "invalid" }] } ->obj.errors.as_p() 此处不展开 ->obj.errors.as_table()此处不展开 ->定制错误信息为中文 ->error_messages ->pwd=forms.CharField(max_length=16,min_length=6,error_messages={'min_length':'太短','max_length':'太长'}) ->error_messages内部字典的key就是as_json()时候的code ->errors中获取某个字段的错误的方法 ->obj.errors['key'] --><ul class="errorlist"><li>Ensure this value has at least 8 characters (it has 4).</li></ul> ->obj.errors['key'][0] -->Ensure this value has at least 8 characters (it has 4). ->模板语言 obj.errors.key.0 ->cleaned_data ->验证成功后,数据被保存在cleaned_data中 ->cleaned_data可以直接被插入数据库 ->models.xxx.obj.creat(**cleaned_data) ->实例:input框的姓名、密码、email的提交 ->自动生成input框 ->输出错误信息(仅文字) -> {{obj.user}}{{obj.user.errors.0}} {{obj.pwd}}{{obj.pwd.errors.0}} {{obj.email}}{{obj.email.errors.0}} ->保留原值 ->x=MYFORM(request.POST) ->form定制显示格式 ->form本身只做验证 ->form生成的html是插件做的 ->class CharField(Field): def __init__(self, *, max_length=None, min_length=None, strip=True, empty_value='', **kwargs): ->class Field: widget = TextInput # Default widget to use when rendering this type of Field. ->class TextInput(Input): input_type = 'text' template_name = 'django/forms/widgets/text.html' -><input type="{{ widget.type }}" name="{{ widget.name }}" {% if widget.value != None %} value="{{ widget.value }}" {% endif %} {% include "django/forms/widgets/attrs.html" %}> ->给字段设置插件 widget=widgets.* ->from django.forms import widgets email=fields.EmailField(error_messages={'invalid':'格式错误'},widget=widgets.Textarea(attrs={'class':'c1','xxx':'xxx2'})) ->设置样式 attrs={'class':c1,'xxx':'xxx2'} ->再来个例子 pwd=fields.CharField(max_length=16,min_length=6,error_messages={'min_length':'太短','max_length':'太长'},widget=widgets.PasswordInput())#不写括号也可以 ->form的常见字段 ->Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否可以编辑 label_suffix=None Label内容后缀 CharField(Field) max_length=None, 最大长度 min_length=None, 最小长度 strip=True 是否移除用户输入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否允许空文件 ImageField(FileField) ... 注:需要PIL模块,pip3 install Pillow 以上两个字典使用时,需要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每一个值进行一次转换 empty_value= '' 空值的默认值 ComboField(Field) fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型 ->常见字段的常用参数 required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 ->obj.user.label initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) ->在后面的项目中有实例 error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) validators=[], 自定义验证规则 ->自定义正则表达式 ->例子: ->username = fields.CharField(max_length=5, validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], ) -> def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号码格式错误') class PublishForm(Form): # 使用自定义验证规则 phone = fields.CharField(validators=[mobile_validate, ], error_messages={'required': '手机不能为空'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'手机号码'})) disabled=False, 是否可以编辑 ->对于各种字段的理解 ->我们主要还是使用CharField来进行定义 ->我们可以通过自定义正则的方式,来自行定义出很多django内部带的或者是没有带的数据类型 ->FileField(Field)文件的处理 ->allow_empty_file=False 是否允许空文件 ->常规的文件处理 ->使用field.FileField()来处理文件 ->x=cleaned_data['Name'] x.name wb x.chunks() ->常用选项插件的处理 ->单选下拉框 ->使用choicesField ->choicesField(choices=[(1,上海),(2,广州),(3,北京)],widget=widgets.Select) ->使用CharField ->user = fields.CharField(initial=2,widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))) ->多选的下拉框 MultipleChoiceField(choices=((1,上海),(2,广州),(3,北京))#列表/元组都可以 ->单选的radio处理 ->使用charField ->user = fields.CharField( initial=2, widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) ) ->使用ChoiceField ->user = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.RadioSelect ) ->单选的checkbox处理 user = fields.CharField(widget=widgets.CheckboxInput()) ->多选checkbox,值为列表 user = fields.MultipleChoiceField( initial=[2, ], choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxSelectMultiple ) ->实例: ->有道云代码 ->流程 ->类 ->字段 (校验) ->插件 (生成html) ->form的初始化操作 initial
dic 界面生成的html则带有内容