第十二章 Django框架
-
tcp/ip五层模型
-
应用层
-
传输层
-
网络层
-
数据链路层
-
物理层
-
-
socket : 套接字,位于应用层和传输层之间的虚拟层,是一组接口
-
c/s架构 ------> b/s架构
#百度浏览器 socket服务器
- 1.创建socket服务端
- 2.绑定ip和接口
- 3.监听
- 4.等待连接
- 7.接受数据
- 8.返回数据
#浏览器 socket客户端
- 5.连接服务端
- 6.发送数据
- 9.接受数据
- 10.断开连接
12.1web框架
12.1.1web框架本质
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。
-
socket服务端
import socket
#创建一个socket对象
sk = socket.socket()
#绑定一个ip和端口
sk.bind(('127.0.0.1',8000))
#监听
sk.listen()
#等待连接
while True:
conn,addr = sk.accept()
#接受数据
conn.recv(1024)
#返回数据
conn.send(b'ok')
#断开连接
conn.close() -
在浏览器中连接上述服务端时出现问题:
我们可以一下可以发现浏览器向服务端发送请求时,无法响应,那么是什么导致这个原因呢?我们想到必须有一个统一的规则,让大家发送消息、接收消息的时候都有个格式依据,不能随便写。这个规则就是HTTP协议.HTTP协议主要规定了客户端和服务器之间的通信格式
让我们首先打印下我们在服务端接收到的消息是什么
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()
while True:
conn, addr = sk.accept()
data = conn.recv(8096)
print(data) # 将浏览器发来的消息打印出来
conn.send(b"OK")
conn.close()
输出
b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=CtHePYARJOKNx5oNVwxIteOJXpNyJ29L4bW4506YoVqFaIFFaHm0EWDZqKmw6Jm8\r\n\r\n'
我们将\r\n替换成换行看得更清晰点:
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: csrftoken=CtHePYARJOKNx5oNVwxIteOJXpNyJ29L4bW4506YoVqFaIFFaHm0EWDZqKmw6Jm8
点击view source之后显示如下图:
12.1.2HTTP协议
HTTP协议,又称超文本传输协议(英文:Hyper Text Transfer Protocol,HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议,
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。HTTP响应的Header中有一个 Content-Type
表明响应的内容格式。它的值如text/html; charset=utf-8。text/html则表示是网页,charset=utf-8则表示编码为utf-8。
12.1.2.1HTTP工作原理
-
HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
-
HTTP 请求/响应的步骤:
-
1.客户端连接到Web服务器
-
2.发送HTTP请求
-
3.服务器接受请求并返回HTTP响应
-
4.释放连接TCP连接
-
5.客户端浏览器解析HTML内容
-
-
在浏览器地址栏键入URL,按下回车之后会经历以下流程:
-
1.浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;
-
2.解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接;
-
3.浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP 请求,该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器;
-
4.服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器;
-
5.释放 TCP连接;
-
6.浏览器解析将该 html 文本并显示内容;
-
12.1.2.2http请求方法
1.GET
-
向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,例如在Web Application中。其中一个原因是GET可能会被网络蜘蛛等随意访问。
2.POST
-
向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求报文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。
3.HEAD
-
与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)。
4.PUT
-
向指定资源位置上传其最新内容。
5.DELETE
-
请求服务器删除Request-URI所标识的资源。
6.TRACE
-
回显服务器收到的请求,主要用于测试或诊断。
7.OPTIONS
-
这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。
8.CONNECT
-
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。
注意:
-
1.方法名称是区分大小写,当某个请求所对应的资源部支持对应方法时,服务器应当返回状态码405(Method Not Allowed),当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。
-
2.HTTP服务器至少应该实现GET和HEAD方法,其他方法都是可选的。当然,所有的方法支持的实现都应当匹配下述的方法各自的语义定义。此外,除了上述方法,特定的HTTP服务器还能够扩展自定义的方法。例如PATCH(由 RFC 5789 指定的方法)用于将局部修改应用到资源
12.1.2.3http状态码
所有HTTP响应的第一行都是状态行,依次是当前HTTP版本号,3位数字组成的状态代码,以及描述状态的短语,彼此由空格分隔。
状态代码的第一个数字代表当前响应的类型:
-
1xx消息——请求已被服务器接收,继续处理
-
2xx成功——请求已成功被服务器接收、理解、并接受
-
3xx重定向——需要后续操作才能完成这一请求
-
4xx请求错误——请求含有词法错误或者无法被执行
-
5xx服务器错误——服务器在处理某个正确请求时发生错误
12.1.2.4资源定位符url
超文本传输协议(HTTP)的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中:
-
传送协议。
-
层级URL标记符号(为[//],固定不变)
-
访问资源需要的凭证信息(可省略)
-
服务器(通常为域名,有时为IP地址)
-
端口号(以数字方式表示,若为HTTP的默认值“:80”可省略)
-
路径(以“/”字符区别路径中的每一个目录名称)
-
查询(GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
-
片段。以“#”字符为起点
以http://www.luffycity.com:80/news/index.html?id=250&page=1 为例:
http默认端口80,https默认端口443
12.1.2.5HTTP请求格式
#请求(request)
#浏览器 —— 》 服务器
############ GET请求 没有请求数据
“请求方式 url路径 协议版本\r\n
k1:v1\r\n
k2:v2\r\n
\r\n
数据”
12.1.2.6HTTP响应格式
#响应(response)
#服务器 —— 》 浏览器
“协议版本 状态码 状态码描述\r\n
k1:v1\r\n
k2:v2\r\n
\r\n
响应数据(响应体)”
12.1.3web框架的功能
-
1.socket收发消息 - wsgiref wsgiref (本地测试) uwsgi(线上)
-
2.根据不同的路径返回不同的内容
-
3.返回动态页面(字符串的替换------->模板的渲染) —— jinja2
django | flask | tornado |
---|---|---|
2 3 | 2 | 1 2 3 |
12.1.4自定义web框架
socket服务端
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",8000))
sk.listen(5)
while True:
conn, addr = sk.accept()
data = conn.recv(8096)
print(data) # 将浏览器发来的消息打印出来
conn.send(b"HTTP/1.1 200 0k\r\n\r\n<h1>OK</h1>")
根据不同的路径返回不同的内容
import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口 sk.listen() # 监听 while True: # 等待连接 conn, add = sk.accept() data = conn.recv(8096) # 接收客户端发来的消息 # 从data中取到路径 data = str(data,encoding="utf-8") # 把收到的字节类型的数据转换成字符串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行 # 根据不同的路径返回不同内容 if url == "/index/": response = b"index" elif url == "/home/": response = b"home" else: response = b"404 not found!" conn.send(response) conn.close()
根据不同的路径返回不同的内容--函数版
import socket # 创建一个socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8000)) # 监听 sk.listen(5) # 等待连接 def index(url): ret = '<h1>index!</h1>({})'.format(url) return ret.encode('utf-8') def home(url): ret = '<h1>home!</h1>({})'.format(url) return ret.encode('utf-8') list1 = [ ('/index', index), ('/home', home), ] while True: conn, addr = sk.accept() # 接收数据 data = conn.recv(1024) data = data.decode('utf-8') url = data.split()[1] #先发送报头 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') func = None for i in list1: if url == i[0]: func = i[1] break if func: ret = func(url) else: ret = b'<h1>404 not found!</h1>' conn.send(ret) # 断开连接 conn.close()
返回具体的HTML文件
import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口 sk.listen() # 监听 # 将返回不同的内容部分封装成不同的函数 def index(url): #创建一个html文件,内容随意 # 读取index.html页面的内容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字节数据 return bytes(s, encoding="utf8") def home(url): #创建一个html文件,内容随意 with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") # 定义一个url和实际要执行的函数的对应关系 list1 = [ ("/index/", index), ("/home/", home), ] while True: # 等待连接 conn, add = sk.accept() data = conn.recv(8096) # 接收客户端发来的消息 # 从data中取到路径 data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行 # 根据不同的路径返回不同内容 func = None # 定义一个保存将要执行的函数名的变量 for item in list1: if item[0] == url: func = item[1] break if func: response = func(url) else: response = b"404 not found!" # 返回具体的响应消息 conn.send(response) conn.close()
让网页动起来
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
sk.listen() # 监听
# 将返回不同的内容部分封装成不同的函数
def index(url):
# 读取index.html页面的内容
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
# 返回字节数据
return bytes(s, encoding="utf8")
def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8")
def timer(url):
import time
with open("time.html", "r", encoding="utf8") as f:
s = f.read()
s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S"))
return bytes(s, encoding="utf8")
# 定义一个url和实际要执行的函数的对应关系
list1 = [
("/index/", index),
("/home/", home),
("/time/", timer),
]
while True:
# 等待连接
conn, add = sk.accept()
data = conn.recv(8096) # 接收客户端发来的消息
# 从data中取到路径
data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
# 按\r\n分割
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
# 根据不同的路径返回不同内容
func = None # 定义一个保存将要执行的函数名的变量
for item in list1:
if item[0] == url:
func = item[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
# 返回具体的响应消息
conn.send(response)
conn.close()
11.2.5服务器程序和应用程序
python web程序一般会分为两部分:服务器程序和应用程序。
-
服务器程序负责对socket服务端进行封装,并在请求到来时,对请求的各种数据进行整理。
-
应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。常用的WSGI服务器有uWSGI、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
11.2.5.1wsgiref
利用wsgiref模块来替换我们自己写的web框架的socket server部分:
""" 根据URL中不同的路径返回不同的内容--函数进阶版 返回HTML页面 让网页动态起来 wsgiref模块版 """ from wsgiref.simple_server import make_server # 将返回不同的内容部分封装成函数 def index(url): # 读取index.html页面的内容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字节数据 return bytes(s, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") def timer(url): import time with open("time.html", "r", encoding="utf8") as f: s = f.read() s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S")) return bytes(s, encoding="utf8") # 定义一个url和实际要执行的函数的对应关系 list1 = [ ("/index", index), ("/home", home), ("/time", timer), ] def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息 url = environ['PATH_INFO'] # 取到用户输入的url func = None for i in list1: if i[0] == url: func = i[1] break if func: response = func(url) else: response = b"404 not found!" return [response, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()
11.2.5.2jinja2
index2.html文件
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>姓名:{{name}}</h1> <h1>爱好:</h1> <ul> {% for hobby in hobby_list %} <li>{{hobby}}</li> {% endfor %} </ul> </body> </html>
使用jinja2渲染index2.html文件:
from wsgiref.simple_server import make_server from jinja2 import Template def index(url): # 读取HTML文件内容 with open("index2.html", "r", encoding="utf8") as f: data = f.read() template = Template(data) # 生成模板文件 ret = template.render({'name': 'alex', 'hobby_list': ['抽烟', '喝酒', '烫头']}) # 把数据填充到模板中 return bytes(ret, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") # 定义一个url和实际要执行的函数的对应关系 list1 = [ ("/index", index), ("/home", home), ] def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息 url = environ['PATH_INFO'] # 取到用户输入的url func = None for i in list1: if i[0] == url: func = i[1] break if func: response = func(url) else: response = b"404 not found!" return [response, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()
使用pymysql连接数据库:
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8") cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select name, age, department_id from userinfo") user_list = cursor.fetchall() cursor.close() conn.close()
模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。
12.2Django
12.2.1 Django框架
12.2.1.1MVC框架和MTV框架
-
MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller),具有耦合性低、重用性高、生命周期成本低等优点。
深入了解:http://www.ruanyifeng.com/blog/2007/11/mvc.html
-
Django框架的不同之处在于它拆分的三部分为:Model(模型)、Template(模板)和View(视图),也就是MTV框架。
12.2.1.2Django的MTV模式
-
Model(模型):负责业务对象与数据库的对象(ORM)
ORM:对象关系映射
-
Template(模版):负责如何把页面展示给用户
-
View(视图):负责业务逻辑,并在适当的时候调用Model和Template
-
此外,Django还有一个urls分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
12.2.2基础命令
12.2.2.1下载安装
-
cmd命令行
-
pip install django==1.11.21
-
pip install django==1.11.21 -i 国内源
-
-
pycharm
-
file--->setting--->
-
12.2.2.2创建项目
-
命令行
-
创建一个文件夹用于存放项目文件,
-
shift + 鼠标右键 ----->点击"在此处打开命令窗口(w)"
-
django-admin startproject 项目名称
项目目录
-
-
pycharm
12.2.2.3启动项目
-
命令行
-
cd 项目根目录 ---------> manage.py
python36 manage.py runserver (默认是80端口)
python36 manage.py runserver 90 在80端口启动
python36 manage.py runserver 0.0.0.0:80` 0.0.0.0:80
如果运行不成功,尝试进行下面操作,然后重启
-
-
pycharm
更改端口和ip
12.2.2.4创建app
-
命令行
-
python36 manage.py startapp app01
-
-
pacharm工具创建
-
tools ——》 run manage.py task ——》 startapp app名称
-
-
第三种创建方式
注册app
INSTALLED_APPS = [ ... 'app01', 'app01.apps.App01Config' #推荐写法 ]
将urls里的两个函数封装到views.py中
然后再urls导入views.py
12.2.2.5数据库迁移
python manage.py makemigrations python manage.py migrate
12.2.2.6创建超级用户
python manage.py createsuperuser
输入以上命令后,根据提示输入用户名、邮箱、密码、确认密码。密码的要求至少是不八位,不能和邮箱太接近,两次密码需要一致。
-
简单使用
模板 ——》HTML 指定文件夹
urls.py
# 导入 from django.shortcuts import HttpResponse,render # 函数 def index(request): # return HttpResponse('index') return render(request,'index.html') # url和函数对应关系 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', index), ]
12.2.3Django基础相关
12.2.3.1Django必备三件套
from django.shortcuts import HttpResponse, render, redirect
-
HttpResponse
-
内部传入一个字符串参数,返回给浏览器。
def index(request): # 业务逻辑代码 return HttpResponse("OK")
-
-
render
-
除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。将数据填充进模板文件,最后把结果返回给浏览器。
def index(request): # 业务逻辑代码 return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})
-
-
redirect
-
接受一个URL参数,表示跳转到指定的URL。
def index(request): # 业务逻辑代码 return redirect("https://www.baidu.com")
重定向
-
-
启动Django报错
Django 启动时报错 UnicodeEncodeError ...
报这个错误通常是因为计算机名为中文,改成英文的计算机名重启下电脑就可以了。
12.2.3.2Django中使用MySQL数据库的流程
-
1.创建一个MySQL数据库。
-
2.在settings中配置,Django连接MySQL数据库:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 引擎 'NAME': 'day53', # 数据库名称 'HOST': '127.0.0.1', # ip地址 'PORT':3306, # 端口 'USER':'root', # 用户 'PASSWORD':'123' # 密码,没有时写成'' } }
-
3.在与settings同级目录下的 init文件中写:
import pymysql pymysql.install_as_MySQLdb()
-
4.创建表结构(在app下的models.py中写类)
from django.db import models class User(models.Model): username = models.CharField(max_lengh=32) #username varchar password = models.CharField(max_lengh=32) # password varchar
-
5.执行数据库迁移的命令
python manage.py makemigrations # 检测每个注册app下的model.py 记录model的变更记录 python manage.py migrate # 同步变更记录到数据库中
创建数据
提交数据
1.orm操作
# 获取表中所有的数据 ret = models.User.objects.all() # QuerySet 对象列表 【对象】 # 获取一个对象(有且唯一) obj = models.User.objects.get(username='alex') # 获取不到或者获取到多个对象会报错 # 获取满足条件的对象 ret = models.User.objects.filter(username='wusir',password='dasb') # QuerySet 对象列表
2.GET和 POST区别
-
从标准上来看
-
GET 用于获取信息,是无副作用的,是幂等的,且可缓存
-
POST 用于修改服务器上的数据,有副作用,非幂等,不可缓存
-
-
从请求报文看
-
GET 和 POST 只是 HTTP 协议中两种请求方式(异曲同工),而 HTTP 协议是基于 TCP/IP 的应用层协议,无论 GET 还是 POST,用的都是同一个传输层协议,所以在传输上,没有区别。
-
报文格式上
-
不带参数时,最大区别就是第一行方法名不同, 仅仅是报文的几个字符不同而已,POST 方法请求报文第一行是这样的 POST /url HTTP/1.1 GET 方法请求报文第一行是这样的 GET /url HTTP/1.1
-
带参数时,在约定中,GET 方法的参数应该放在 url 中,POST 方法参数应该放在 body 中
-
-
-
GET产生一个TCP数据包;POST产生两个TCP数据包。
12.2.4Django模板
官方文档链接:https://docs.djangoproject.com/en/1.11/ref/templates/language/
Django模板中只需要记两种特殊符号:{{ }}表示变量,在模板渲染的时候替换成值,{% %}表示标签,tag,表示逻辑相关的操作。
12.2.4.1常用语法
1.变量
-
{{ 变量名 }}
-
变量名由字母数字和下划线组成。
-
点(.)在模板语言中有特殊的含义,用来获取对象的相应属性值。
例:view中代码
from django.shortcuts import render def template_text(request): string = 'dasb' age = 73 hobby = ['抽烟','喝酒','烫头'] dic = {'name':'dalao','age':age,'hobby':hobby} return render(request,"template_test.html",{'string':string,'age':age,'hobby':hobby,'dic':dic})
模板中支持的写法:
{# 取l中的第一个参数 #} {{ l.0 }} {# 取字典中key的值 #} {{ d.name }} {# 取对象的name属性 #} {{ person_list.0.name }} {# .操作只能调用不带参数的方法 #} {{ person_list.0.dream }}
注:当模板系统遇到一个(.)时,会按照如下的顺序去查询:
-
1.在字典中查询
-
2.属性或者方法
-
3.数字索引
-
2.Filter
-
过滤器,用来修改变量的显示结果。
-
语法 : {{ value|filter_name:参数}}
-
':'左右没有空格
-
default
-
{{ value|default:"nothing"}}
-
如果变量value不存在或者变量为空的话就显示nothing
-
注:TEMPLATES的OPTIONS可以增加一个选项:string_if_invalid:"变量不存在",可以替代default的的作用。
-
1.filesizeformat
-
文件大小格式化
-
{{ value|filesizeformat }}
-
范围 : byte ~ PB
2.add
-
{{ value|add:"2" }}
-
例
{{2|add:4}} # 6 {{"2"|add:4}} # 6 {{"2"|add:"4"}} # 6 {{"2"|add:"asdf"}} #2asdf {{ first|add:second }} #如果first是 [1,.2,3] ,second是 [4,5,6] ,那输出结果是 [1,2,3,4,5,6] #如果first是 ["抽烟",'喝酒',"烫头"] ,second是 ["唱","跳","rap",'篮球'] ,那输出结果是 ["抽烟",'喝酒',"烫头","唱","跳","rap",'篮球'] #Django模版加法: {{ value|add:10}} value=5,则返回15 #Django模版减法: {{value|add:-10}} value=5,则返回-5,这个比较好理解,减法就是加一个负数 #Django模版乘法和除法: {% widthratio 5 1 100 %} 上面的代码表示:5/1 *100,返回500,widthratio需要三个参数,它会使用 参数1/参数2*参数3,所以要进行乘法的话,就将参数2=1即可 #数据保留两位小数 <td>{{ foo.product_amount|floatformat:5 }}</td> register = template.Library() #一些复杂一些的运算 利用 add 这个filter ,可以做更疯狂的事: 计算 A^2: {% widthratio A 1 A %} 计算 (A+B)^2: {% widthratio A|add:B 1 A|add:B %} 计算 (A+B) * (C+D): {% widthratio A|add:B 1 C|add:D %} #除法并保留小数 首先定义方法在templatehelper.py文件中 @register.filter def div(value, div): ''' 分转化为元,保留两位小数 :param value: :param div: :return: ''' return round((value / div), 2) 然后在模板中可以按照如下使用,当然前提是{% load templatehelper %}: <td>{{ foo.product_amount |div:100 }}</td>
3.lower
-
全小写
-
{{value|lower}}
4.upper
-
全大写
-
{{value|upper}}
5.title
-
标题,首字母大写
-
{{value|title}}
6.length
-
计算长度
-
{{value|length}}
7.slice
-
切片
hobby = ["抽烟",'喝酒',"烫头","唱","跳","rap",'篮球'] {{hobby|slice:'-2:0:-1'}} #["rap","跳","唱","烫头",'喝酒']
8.first和last
9.join
-
使用字符串拼接列表。同python的str.join(list)。
-
{{ value|join:" // " }}
10.truncatechars
-
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
-
参数:截断的字符数 {{ value|truncatechars:9}}
long_str = "you have 13 years to go to anthers" {{long_str|truncatechars:10}} # you hav... {{long_str|truncatechars:3}} # ... {{long_str|truncatewords:3}} # you have 13 years... long_str = "咱也不知道,咱也不敢问" {{long_str|truncatewords:3}} #咱也不知道,咱也不敢问 (不识别中文) {{long_str|truncatechars:3}} # ... {{long_str|truncatechars:4}} # 咱...
11.date
-
日期格式化
-
{{ value|date:"Y-m-d H:i:s"}}
可格式化输出的字符链接网址:https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#date
-
另一种修改方式
setting中的配置:
USE_L10N = False DATETIME_FORMAT = 'Y-m-d H:i:s'
12.safe
-
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。
value = "<a href='#'>点我</a>" {{ value|safe}}
3.自定义Filter
-
1.在app01下创建一个名为templatetags的文件包(必须为此名字)
-
2.在该python包创建py文件,文件名自定义(my_tags.py);
-
3..在py文件中写:
from django import template register = template.Library() # register也不能变
-
4.写函数+装饰器
@register.filter def add_xx(value, arg): # 最多有两个 return '{}-{}'.format(value, arg)
-
5.使用:
{% load my_tags %} {{ 'alex'|add_xx:'dsb' }}
错误
解决: 一:重启项目
4Tags
1.for
<ul> {% for hobby in hobby_list %} <li> {{ forloop.counter }}.{{ hobby }} </li> {% endfor %} </ul>
-
for循环可用的一些参数:
Variable Description forloop.counter
当前循环的索引值(从1开始) forloop.counter0
当前循环的索引值(从0开始) forloop.revcounter
当前循环的倒序索引值(到1结束) forloop.revcounter0
当前循环的倒序索引值(到0结束) forloop.first
当前循环是不是第一次循环(布尔值) forloop.last
当前循环是不是最后一次循环(布尔值) forloop.parentloop
本层循环的外层循环 偶数列为红色
<table border="1"> <tbody> {% for hobby_list1 in hobby_list2 %} <tr> {% for hobby in hobby_list1 %} {% if forloop.counter|divisibleby:2 %} <td style="color: red;">{{ hobby }}</td> {% else %} <td >{{ hobby }}</td> {% endif %} {% endfor %} </tr> {% endfor %} </tbody> </table>
2.for....empty
<ul> {% for user in user_list %} <li>{{ user.name }}</li> {% empty %} <li>空空如也</li> {% endfor %} </ul>
3.if
-
if,elif和else
-
if,else
-
if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。
{% if age < 73 %} 还没到73岁 {% elif age == 73 %} 刚好73岁 {% elif age > 73 %} 过了73岁 {% endif %}
注意 :
-
python中 :10>5>1 =》 10>5 and 5>1 true
-
js :10>5>1 =》 10>5 =》 true =》 1>1 false
-
模板中 不支持连续连续判断 也不支持算数运算(可用过滤器判断)
-
4.with
#写法一:只在with里生效 {% with hobby_list2.2.3 as hobby %} {{ hobby }} {{ hobby }} {{ hobby }} {% endwith %} <br> #写法二:只在with里生效 {% with hobby=hobby_list2.2.3 %} {{ hobby }} {{ hobby }} {{ hobby }} {% endwith %}
5.注释
<!--{{'不显示'}}-->
12.2.4.2母版和继承
1.母版
-
就是一个普通HTML提取多个页面的公共部分 定义block块
-
继承母版和块(block)
-
{% extends ‘base.html’ %}
-
重写block块 —— 写子页面独特的内容
-
#注意: 1.{% extends 'base.html' %} 写在第一行 前面不要有内容 有内容会显示 2.把要显示的内容写在block块中 3.{% extends 'base.html' %} 'base.html' 需要加上引号 不然当做变量去查找 4.定义多个block块,定义 css js 块
12.2.4.3组件
可以将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方按如下语法导入即可。(例: "nav.html")
{% include 'navbar.html' %}
12.2.4.4静态文件相关
{% load static %} /*{% load staticfiles %} */ <link rel="stylesheet" href="{% static '/plugins/bootstrap-3.3.7/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static '/css/dsb.css' %}"> /*另一种写法*/ <link rel="stylesheet" href="{% get_static_prefix %}/css/dsb.css"> {% static '/plugins/bootstrap-3.3.7/css/bootstrap.css' %} {% get_static_prefix %} ——》 获取别名
1.自定义simple_tag
-
和自定义filter类似,只不过接收更灵活的参数。
@register.simple_tag def join_str(*args, **kwargs): return '{} - {} '.format('*'.join(args), '$'.join(kwargs.values()))
#使用 {% load my_tags %} {% join_str '1' '2' k1='3' k2='4' %}
2.inclusion_tag
-
返回一个html代码段
3.静态文件的配置
在settings.py写入下面代码
STATIC_URL = '/static/' # 别名 ,HTML中使用的静态文件夹前缀 STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static1'), ## 静态文件存放位置,按照STATICFILES_DIRS列表的顺序进行查找。 os.path.join(BASE_DIR, 'static'), os.path.join(BASE_DIR, 'static2'), ]
<link rel="stylesheet" href="/static/css/login.css"> # 别名开头
12.2.5Django的View(视图)
-
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
-
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。
-
为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为
views.py
的文件中。
12.2.5.1CBV和FBV
-
FBV function based view (基于函数的视图)
-
CBV class based biew (基于类的视图)
FBV写法:
from django.shortcuts import HttpResponse,render,redirect #新增出版社 def add_publisher(request): #对请求方式进行判断 error = '' if request.method == 'POST': #处理POST请求 #获取出版社名称 publisher_name = request.POST.get('publisher_name') #判断出版社名称是否重复 if models.publisher.objects.filter(name=publisher_name): error = "出版社名称已存在" #判断输入值是否为空 if not publisher_name: error = "输入不能为空" if not error: #使用orm将数据插入到数据库中 obj = models.publisher.objects.create(name=publisher_name) print(obj.pk,obj.name) #跳转到展示出版社界面 return redirect('/publisher_list/') #返回一个包含form表单的页面 return render(request,'add_publisher.html',{'error':error})
CBV 写法
from django.shortcuts import HttpResponse,render,redirect #类方法写新增出版社 from django.views import View class Addpublisher(View): def get(self,request): #处理get请求 return render(request,'add_publisher.html') def post(self,request): # 处理post请求 # 处理POST请求 error = '' # 获取出版社名称 publisher_name = request.POST.get('publisher_name') # 判断出版社名称是否重复 if models.publisher.objects.filter(name=publisher_name): error = "出版社名称已存在" # 判断输入值是否为空 if not publisher_name: error = "输入不能为空" if not error: # 使用orm将数据插入到数据库中 obj = models.publisher.objects.create(name=publisher_name) print(obj.pk, obj.name) # 跳转到展示出版社界面 return redirect('/publisher_list/') return render(request,'add_publisher.html',{'error':error})
#路径修改为 url(r'^add_publisher/', views.Addpublisher.as_view()),
-
as_view的流程
-
1.项目启动,加载urls.py时,执行 类.as_view() ,得到了一个view函数
-
2.请求到来时执行view函数
-
1.实例化类------->对象 selef.request = request
-
2.执行self.dispatch(request, *args, **kwargs)
-
判断请求方式是否被允许
-
允许 :通过反射获取到对应请求方式的方法,赋值给handler
-
不允许:将self.http_method_not_allowed赋值给handler
-
-
执行handler(request,*args,**kwargs),返回结果给jango,再给浏览器
-
-
-
12.2.5.2视图加装饰器
-
FBV : 直接加装饰器
from django.shortcuts import HttpResponse,render,redirect from app01 import models import time def timer(func): def inner(*args,**kwargs): start = time.time() ret = func(*args,**kwargs) print('函数执行时间为{}'.format(time.time()-start)) return ret return inner #展示出版社 @timer def publisher_list(request): #从数据库中查询出版社信息 all_publishers = models.publisher.objects.all().order_by("pk") #返回一个包含出版社信息的页面 return render(request,'publisher_list.html',{'all_publishers':all_publishers})
-
CBV :
from django.shortcuts import HttpResponse,render,redirect from app01 import models from django.utils.decorators import method_decorator import time def timer(func): def inner(*args,**kwargs): start = time.time() ret = func(*args,**kwargs) print('函数执行时间为{}'.format(time.time()-start)) return ret return inner
-
加在方法上
class Addpublisher(View): @method_decorator(timer) def get(self,request): #处理get请求 return render(request,'add_publisher.html')
-
加在自己写的dispatch上(推荐)
class Addpublisher(View): @method_decorator(timer) def dispatch(self,request,*args,**kwargs): ret = super().dispatch(request,*args,**kwargs) return ret
-
加在类上
@method_decorator(timer,name='get') @method_decorator(timer,name='post') class Addpublisher(View): def dispatch(self,request,*args,**kwargs): ret = super().dispatch(request,*args,**kwargs) return ret def get(self,request): pass def post(self,request,*args,**kwargs): pass
#还可以这样写 @method_decorator(timer,name='dispatch') def get(self,request): pass def post(self,request,*args,**kwargs): pass
不使用method_decorator也会出结果
注:使用或者不使用method_decorator的区别
-
不使用method_decorator
-
func :结果为 <function AddPublisher.get at 0x000001FC8C358598>
-
args : 结果为 (<app01.views.AddPublisher object at 0x000001FC8C432C50>, <WSGIRequest: GET '/add_publisher/'>)
-
-
使用method_decorator
-
func ——》 <function method_decorator.<locals>.dec.<locals>.wrapper.<locals>.bound_func at 0x0000015185F7C0D0>
-
args ——》 (<WSGIRequest: GET '/add_publisher/'>,)
-
-
-
12.2.5.3request对象
-
当一个页面被请求时,Django就会创建一个包含本次请求原信息的HttpRequest对象。Django会将这个对象自动传递给响应的视图函数,一般视图函数约定俗成地使用 request 参数承接这个对象。官方文档链接:https://docs.djangoproject.com/en/1.11/ref/request-response/
-
请求常用值
request.method 请求方法,请求中使用的HTTP方法的字符串表示,全大写 request.GET url上携带的参数,包含所有HTTP GET参数的类字典对象 request.POST post请求携带的数据,包含所有HTTP POST参数的类字典对象 request.path request.path_info URL的路径 不包含ip和端口 不包含参数 request.body 请求体 b''类型
-
属性
django将请求报文中的请求行、头部信息、内容主体封装成 HttpRequest 类中的属性。除了特殊说明的之外,其他均为只读的。
序号 属性 结果 1 request.scheme 表示请求方案的字符串(通常为http或https) 2 request.body 请求体为 b''类型 3 request.path 一个字符串,表示请求的路径组件(不含域名)。 4 request.method 一个字符串,表示请求使用的HTTP 方法。必须使用大写 5 request.encoding 一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 'utf-8') 6 request.GET url上携带的参数,包含所有HTTP GET参数的QueryDict 对象 7 request.POST post请求携带的数据,包含所有HTTP POST参数的QueryDict对象 8 request.COOKIES 一个标准的Python 字典,包含所有的cookie。键和值都为字符串 9 request.FILES 一个类似于字典的对象,包含所有的上传文件信息。 FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会 包含数据。否则,FILES 将为一个空的类似于字典的对象。 10 request.META 一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器 11 request.user 一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。(模块) 12 request.session 一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。 -
request.FILES
<form action="" method="post" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="f1"> <button>上传</button> </form>
def upload(request): if request.method == 'POST': #获取文件 print(request.FILES) #<MultiValueDict: {'f1': [<InMemoryUploadedFile: S80903-190319(1).jpg (image/jpeg)>]}> f1 = request.FILES.get('f1') #保存文件 with open(f1.name ,'wb') as f: for i in f1.chunks(): f.write(i) return render(request,'upload.html')
-
-
方法
request.get_full_path() URL的路径 不包含ip和端口 包含参数 request.get_host() 获取主机的ip和端口 request.is_secure() request.is_ajax() 判断是都是ajax请求
12.2.5.4response对象
HttpResponse('字符串') #返回字符串 content-type = 'text/html' render(request,'模板文件名',{k1:v1}) #返回一个完整的html页面 redirect('重定向地址') #重定向 本质 : 响应头Location:地址
12.2.5.5JsonResponse对象
from django.http.response import JsonResponse import json def json_data(request): data = {'name':'liming','age':18} ret = HttpResponse(json.dumps(data)) ret['Content-Type'] = application/json return ret #return HttpResponse(json.dumps(data)) #Content-Type: text/html; charset=utf-8 return JsonResponse(data) #Content-Type: application/json 会自己做反序列化
from django.http.response import JsonResponse def json_data(request): #当被传入数据为列表时,要加上safe=False data = [1,2,3,4] return JsonResponse(data,safe=False)
12.2.6Django的路由系统
URL配置(URLconf)就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表。我们就是以这种方式告诉Django,遇到哪个URL的时候,要对应执行哪个函数。
12.2.6.1URLconf配置
-
基本格式
from django.conf.urls import url urlpatterns = [ url(正则表达式, views视图,参数,别名), ]
参数说明
-
正则表达式:一个正则表达式字符串
-
views视图:一个可调用对象,通常为一个视图函数
-
参数:可选的要传递给视图函数的默认参数(字典形式)
-
别名:一个可选的name参数
-
-
示例
#1.0写法 from django.conf.urls import url from . import views urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/([0-9]{4})/$', views.year_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
#2.0写法 from django.urls import path,re_path urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ]
Django 2.0版本中的路由系统是下面的写法官方链接:https://docs.djangoproject.com/en/1.11/topics/http/urls/
12.2.6.2正则表达式
from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^blog/', views.blog), url(r'^blog/[0-9]{4}', views.blogs), ]
-
注意事项
-
1.urlpatterns中的元素按照书写顺序从上到下逐一匹配正则表达式,一旦匹配成功则不再继续
-
2.不需要添加一个签到的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
-
3.每个正则表达式前面的"r"可选但建议加上
-
4.若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)
-
-
补充说明
# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项 APPEND_SLASH=True
如果在settings.py中设置了 APPEND_SLASH=False,此时我们再请求 时就会提示找不到页面。
Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。
谷歌浏览器清除缓存方式 : ctrl + shift + delete
12.2.6.3分组命名匹配
1.分组和命名分组
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/([0-9]{4})', views.blogs), #分组,将捕获的参数按位置传参传递给视图函数
url(r'^blog/(?P<year>[0-9]{4})', views.blogs) #命名组,将捕获的参数按关键字传参传递给视图函数
]
项目的修改:
2.捕获的参数永远都是字符串
每个在URLconf中捕获的参数都作为一个普通的Python字符串传递给视图,无论正则表达式使用的是什么匹配方式。例如,下面这行URLconf 中:
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
传递到视图函数views.year_archive()
中的year
参数永远是一个字符串类型
3.视图函数中的指定默认值
# urls.py中 from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/$', views.page), url(r'^blog/page(?P<num>[0-9]+)/$', views.page), ] # views.py中,可以为num指定默认值 def page(request, num="1"): pass
在上面的例子中,两个URL模式指向相同的view - views.page - 但是第一个模式并没有从URL中捕获任何东西。如果第一个模式匹配上了,page()函数将使用其默认参数num=“1”,如果第二个模式匹配,page()将使用正则表达式捕获到的num值。
4.include其他的URLconfs
urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^app01/', include('app01.urls')), ]
app01.urls
from django.conf.urls import url from . import views urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^blog/$', views.blog), url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blogs), ]
12.2.6.4传递额外的参数给视图函数(了解)
django.conf.urls.url()
可以接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。
from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ]
在这个例子中,对于/blog/2005/请求,Django 将调用views.year_archive(request, year='2005', foo='bar')。 当传递额外参数的字典中的参数和URL中捕获值的命名关键字参数同名时,函数调用时将使用的是字典中的参数,而不是URL中捕获的参数。
12.2.6.5命名URL和URL反向解析
1.命名
静态路由
from django.conf.urls import url,include from django.contrib import admin from . import views urlpatterns = [ url(r'blog',views.blog,name='blog') ]
反向解析
app01 / urls.py
from django.conf.urls import url,include from django.contrib import admin from . import views urlpatterns = [ #url(r'^admin/', admin.site.urls), url(r'blog',views.blog,name='blog'), url(r'index',views.index) ]
模板
<a href="{% url 'blog' %}">博客</a>
python文件中
from django.shortcuts import reverse def blog(request): url = reverse('index') print(url,type(url)) return HttpResponse('OK')
动态路由
模板中
{% url 'blogs' 2222 12 %}" ——》 /blog/2222/12/ {% url 'blogs' year=2222 month=12 %}" ——》 /blog/2222/12/
py文件中
from django.urls import reverse reverse('blogs',args=('2019','06')) ——》 /app01/blog/2019/06/ reverse('blogs',kwargs={'year':'2019','month':'06'}) ——》 /app01/blog/2019/06/
12.2.6.6命名空间模式
即使不同的APP使用相同的URL名称,URL的命名空间模式也可以让你唯一反转命名的URL。
例:project中的urls.py
from django.conf.urls import url, include urlpatterns = [ url(r'^app01/', include('app01.urls', namespace='app01')), url(r'^app02/', include('app02.urls', namespace='app02')), ]
app01中的urls.py
from django.conf.urls import url from app01 import views app_name = 'app01' urlpatterns = [ url(r'^(?P<pk>\d+)/$', views.detail, name='detail')
app02中的urls.py
from django.conf.urls import url from app02 import views app_name = 'app02' urlpatterns = [ url(r'^(?P<pk>\d+)/$', views.detail, name='detail')
现在,我的两个app中 url名称重复了,我反转URL的时候就可以通过命名空间的名称得到我当前的URL。
语法:'命名空间名称:URL名称'
模板中使用:
{% url 'app01:detail' pk=12 pp=99 %}
views中的函数中使用
v = reverse('app01:detail', kwargs={'pk':11})
这样即使app中URL的命名相同,我也可以反转得到正确的URL了。
12.2.7模型(ORM)
12.2.7.1ORM概念
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中,ORM在业务逻辑层和数据库层之间充当了桥梁的作用。
ORM的优势 | ORM的劣势 |
---|---|
ORM解决的主要问题是对象和关系的映射。它通常将一个类和一张表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。 ORM提供了对数据库的映射,不用直接编写SQL代码,只需操作对象就能对数据库操作数据。让软件开发人员专注于业务逻辑的处理,提高了开发效率。 | ORM的缺点是会在一定程度上牺牲程序的执行效率。ORM的操作是有限的,也就是ORM定义好的操作是可以完成的,一些复杂的查询操作是完成不了。ORM用多了SQL语句就不会写了,关系数据库相关技能退化... |
12.2.7.2Django中的ORM
1.常用字段
序号 | 常用字段 | 含义 |
---|---|---|
1 | AutoField | 自增的整形字段,必填参数primary_key=True,则成为数据库的主键。无该字段时,django自动创建。一个model不能有两个AutoField字段。 |
2 | CharField | 字符类型,必须提供max_length参数。max_length表示字符的长度。 |
3 | IntegerField | 一个整数类型。数值的范围是 -2147483648 ~ 2147483647。 |
4 | DateField | 日期类型,日期格式为YYYY-MM-DD,相当于Python中的datetime.date的实例。 |
5 | DatetimeField | 日期时间字段,格式为YYYY-MM-DD HH:MM:ss[.uuuuuu],相当于Python中的datetime.datetime的实例。 |
6 | DecimalField | 10进制小数,参数:max_digits,小数总长度5位 decimal_places,小数位长度2位 |
7 | BooleanField | 布尔值类型 |
8 | TextField | 文本 |
-
BooleanField和DecimalField中属性
-
auto_now = True:新增、修改数据的时候会自动保存当前的时间
-
auto_now_add = True:新增数据的时候会自动保存当前的时间
-
default 默认值
-
三者互斥,只能取其一
-
-
其它字段
AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 primary_key=True 注:当model中如果没有自增列,则自动会创建一个列名为id的列 from django.db import models class UserInfo(models.Model): # 自动创建一个列名为id的且为自增的整数列 username = models.CharField(max_length=32) class Group(models.Model): # 自定义自增列 nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) SmallIntegerField(IntegerField): - 小整数 -32768 ~ 32767 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正小整数 0 ~ 32767 IntegerField(Field) - 整数列(有符号的) -2147483648 ~ 2147483647 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正整数 0 ~ 2147483647 BigIntegerField(IntegerField): - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 NullBooleanField(Field): - 可以为空的布尔值 CharField(Field) - 字符类型 - 必须提供max_length参数, max_length表示字符长度 EmailField(CharField): - 字符串类型,Django Admin以及ModelForm中提供验证机制 IPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制 GenericIPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 - 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both" URLField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证 URL SlugField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) CommaSeparatedIntegerField(CharField) - 字符串类型,格式必须为逗号分割的数字 UUIDField(Field) - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 FilePathField(Field) - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 - 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 FileField(Field) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage ImageField(FileField) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串) DateTimeField(DateField) - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field) - 日期格式 YYYY-MM-DD TimeField(DateTimeCheckMixin, Field) - 时间格式 HH:MM[:ss[.uuuuuu]] DurationField(Field) - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 FloatField(Field) - 浮点型 BinaryField(Field) - 二进制类型
2.自定义字段
#app01/models.py class MyCharField(models.Field): """ 自定义的char类型的字段类 """ def __init__(self, max_length, *args, **kwargs): self.max_length = max_length super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs) def db_type(self, connection): """ 限定生成数据库表的字段类型为char,长度为max_length指定的值 """ return 'char(%s)' % self.max_length
#使用自定义char类型字段 class Class(models.Model): id = models.AutoField(primary_key=True) title = models.CharField(max_length=25) # 使用自定义的char类型的字段 cname = MyCharField(max_length=25)
3.字段参数
1.null 数据库中字段是否可以为空 2.db_index 索引 3.blank form表单中是否允许用户输入为空 4.db_index 数据库中字段是否可以建立索引 5.db_column 数据库中字段的列名 6.default 数据库中字段的默认值 7.primary_key 数据库中字段是否为主键 8.unique 数据库中字段是否可以建立唯一索引 9.verbose_name Admin中显示的字段名称 unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引 unique_for_month 数据库中字段【月】部分是否可以建立唯一索引 unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 editable Admin中是否可以编辑(不显示) help_text Admin中该字段的提示信息 choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
超级用户的创建
from django.contrib import admin from app01 import models admin.site.register(models.Person)
class Person(models.Model): pid = models.AutoField(primary_key=True) name = models.CharField(max_length=32,db_column='username',verbose_name='姓名') #会将数据库中字段名字修改为username age = models.IntegerField() birth = models.DateTimeField(auto_now_add=True) phone = MyCharField(max_length=11,blank=True,null=True,verbose_name='手机号') gender = models.BooleanField('性别',choices=((0,'女'),(1,'男')))
4.表的参数
class Person(models.Model): pid = models.AutoField(primary_key=True) age = models.IntegerField() class Meta: db_table = 'person' #表名 verbose_name = '个人信息' verbose_name_plural = '个人信息' # 联合索引 index_together = [("name", "age"), ] # 应为两个存在的字段 # 联合唯一索引 unique_together = (("name", "age"),) # 应为两个存在的字段
12.2.7.4ORM操作
官方文档链接:https://docs.djangoproject.com/en/1.11/ref/models/querysets/
1.一般操作(必知必会13条)
#返回 Queryset all filter exclude values values_list order_by reverse distinct() #返回对象 get first last #返回数字 count #返回布尔值 exists
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings") import django django.setup() from app01 import models #1 .all() 获取所有的数据 ---->>QuerySet 对象列表 ret = models.Person.objects.all() print(ret) #<QuerySet [<Person: 1-alex>, <Person: 2-eric>, <Person: 3-zhang>, <Person: 4-li>]> #2 .get(**kwargs) 获取满足条件的一个数据 --->> 对象 #获取不到或多个都会报错 ret = models.Person.objects.get(pk=1) print(ret) #1-alex #3 .filter(**kwargs) 获取满足条件的所有数据 ---->>QuerySet 对象列表 #若没有,就返回一个空列表 ret = models.Person.objects.filter(gender=0) print(ret) #<QuerySet [<Person: 1-alex>, <Person: 2-eric>, <Person: 3-zhang>]> #4 .exclude(**kwargs) 排除不满足条件的所有数据 ---->>QuerySet 对象列表 ret = models.Person.objects.exclude(pk=1) print(ret) #<QuerySet [<Person: 2-eric>, <Person: 3-zhang>, <Person: 4-li>]> #5 .values() 拿到对象所有的字段和字段的值---->>QuerySet [{},{}.{}] 内为字典 #.values('字段名称') 拿到对象指定的字段和字段的值---->>QuerySet [{},{}.{}] ret = models.Person.objects.values() for i in ret: print(i) ret = models.Person.objects.values('pid','name') for i in ret: print(i) #6 .values_list() 拿到对象所有的字段和字段的值---->>QuerySet [(),().()] 内为元组 # .values_list('字段名称') 拿到对象指定的字段和字段的值---->>QuerySet [(),().()] 内为元组 ret = models.Person.objects.values_list() for i in ret: print(i) ret = models.Person.objects.values_list('pid', 'name') #结果按()内给定位置排序 for i in ret: print(i) #7 .order_by('字段名称') 对查询结果排序---->>QuerySet ret = models.Person.objects.all().order_by('pk') #升序 print(ret) #<QuerySet [<Person: 1-alex>, <Person: 2-eric>, <Person: 3-zhang>, <Person: 4-li>]> ret = models.Person.objects.all().order_by('-pk') # 降序 print(ret) #<QuerySet [<Person: 4-li>, <Person: 3-zhang>, <Person: 2-eric>, <Person: 1-alex>]> ret = models.Person.objects.all().order_by('age','-pid') # 降序 print(ret) #<QuerySet [<Person: 1-alex>, <Person: 3-zhang>, <Person: 2-eric>, <Person: 4-li>]> #8 .reverse() 反向排序(只能对已经排序的QuerySet进行排序) ret = models.Person.objects.all() # 升序 print(ret) #<QuerySet [<Person: 1-alex>, <Person: 2-eric>, <Person: 3-zhang>, <Person: 4-li>]> ret = models.Person.objects.all().order_by('pk').reverse() print(ret) #<QuerySet [<Person: 4-li>, <Person: 3-zhang>, <Person: 2-eric>, <Person: 1-alex>]> #9 .distinct() 去重(只有在PostgreSQL中支持按字段去重)完全相同的内容才能去重 ret = models.Person.objects.all().distinct() print(ret) #10 .count() 计数 ret = models.Person.objects.all().count() print(ret) #11 .first() 取第一个元素,没有就为None ret = models.Person.objects.filter(pk=1).first() print(ret) ret = models.Person.objects.filter(pk=1).values().first() print(ret) #12 .last() 取最后一个元素,没有就为None ret = models.Person.objects.filter(pk=1).first() print(ret) #13 .exists() 查询的数据是否存在 ret = models.Person.objects.filter(pk=100).first() print(ret)
2.单表的双下划綫
import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings") import django django.setup() from app01 import models ret = models.Person.objects.filter(pk=1) print(ret) ret = models.Person.objects.filter(pk__gt=1) #gt greater than 大于 print(ret) ret = models.Person.objects.filter(pk__lt=3) #gt less than 小于 print(ret) ret = models.Person.objects.filter(pk__gte=1) #gt greater than equal 大于等于 print(ret) ret = models.Person.objects.filter(pk__lte=3) #gt less than equal 小于等于 print(ret) ret = models.Person.objects.filter(pk__range=[2,3]) # 2<=pk<=3连续 print(ret) ret = models.Person.objects.filter(pk__in=[1,3]) # 成员判断,在列表里,1和3 print(ret) ret = models.Person.objects.filter(name__contains='a') # 获取name字段包含"a"的 print(ret) ret = models.Person.objects.filter(name__contains='a') # icontains大小写不敏感 print(ret) ret = models.Person.objects.filter(name__startswith='a') # name字段以'a'开头 print(ret) ret = models.Person.objects.filter(name__istartswith='a') # 大小写不敏感 print(ret) ret = models.Person.objects.filter(name__endswith='a') # name字段以'a'结尾 print(ret) ret = models.Person.objects.filter(name__iendswith='a') # 大小写不敏感 print(ret) ret = models.Person.objects.filter(birth__year='2019') #出生年份为2019年(不支持月份和日) print(ret) ret = models.Person.objects.filter(birth__contaits='2019-06') print(ret) ret = models.Person.objects.filter(phone__isnull=True) print(ret)
3.外键操作
import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings") import django django.setup() from app01 import models #基于对象的查询 #正向 book_obj = models.Book.objects.get(title='菊花怪大战xcq') print(book_obj.pub) #老男孩出版社 #反向 pub_obj = models.Publisher.objects.get(pk=1) #print(pub_obj.book_set.all(),type(pub_obj.book_set)) #未指定related_name时,使用类名小写_set set理解为集合 #print(pub_obj.books.all(),type(pub_obj.book_set)) #指定related_name='books'时 #基于字段的查询 ret = models.Book.objects.filter(title='菊花怪大战xcq') print(ret) #查询老男孩出版社的书 ret = models.Book.objects.filter(pub__name='老男孩出版社') print(ret) #查询出版'菊花怪大战xcq'的出版社 #有related_name='books' ret = models.Publisher.objects.filter(books__title='菊花怪大战xcq') print(ret) #无related_name='books' 类名小写 ret = models.Publisher.objects.filter(book__title='菊花怪大战xcq') print(ret) #related_name='books',related_query_name='xxx' ret = models.Publisher.objects.filter(xxx__title='菊花怪大战xcq') print(ret)
12.2.7.5ManyToManyField
关系管理对象
import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings") import django django.setup() from app01 import models #基于对象的查询 mjj = models.Author.objects.get(pk=1) print(mjj.books) #关系管理对象 print(mjj.books.all()) book_obj = models.Book.objects.filter(title='桃花侠大战菊花怪').first() #不指定related_name #print(book_obj.author_set) #print(book_obj.author_set.all()) #指定related_name print(book_obj.authors.all()) ret = models.Author.objects.filter(books__title='桃花侠大战菊花怪') print(ret)
mjj = models.Author.objects.get(pk=1) #1 .all() 所关联的所有对象 print(mjj.books.all()) #2 .set() 设置多对多关系 [id] [对象] #mjj.books.set([1,2,3]) mjj.books.set(models.Book.objects.filter(pk__in=[1,2,3])) #3 .add 添加多对对关系 [id] [对象] mjj.books.add(4,5) mjj.books.add(*models.Book.objects.filter(pk__in=[4,5])) #4 .remove 删除多对多关系[id] [对象] mjj.books.remove(4,5) mjj.books.remove(*models.Book.objects.filter(pk__in=[4,5])) #5 .clear 清楚所有多对多关系 mjj.books.clear() #6 .create() obj = mjj.books.create(title='菊花怪大战xcq',pub_id=1) print(obj)
12.2.7.6其他操作
1.在Python脚本中调用Django环境
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") import django django.setup() from app01 import models books = models.Book.objects.all() print(books)
12.2.7.7聚合查询和分组查询
1.聚合查询
-
aggregate()
是QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典,键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的 from django.db.models import Max,Min,Avg,Sum,Countimport os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings") import django django.setup() from app01 import models from django.db.models import Max,Min,Avg,Sum,Count ret = models.Book.objects.all().aggregate(Max('price')) print(ret) ret = models.Book.objects.all().aggregate(Avg('price')) print(ret) ret = models.Book.objects.all().aggregate(Avg('price'),max=Max('price')) print(ret) ret = models.Book.objects.filter(pk__gt=2).aggregate(Avg('price'),max=Max('price')) print(ret)
2.分组查询
我们在这里先复习一下SQL语句的分组:
我们使用原生SQL语句,按照部门分组求平均工资:
select dept,avg(salary) from employee group by dept;
ORM查询:
from django.db.models import Avg Employee.objects.values("dept").annotate(avg=Avg("salary").values("dept", "avg")
SQL查询:
select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;
ORM查询:
from django.db.models import Avg models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings") import django django.setup() from app01 import models from django.db.models import Max,Min,Avg,Sum,Count
#示例1:统计每一本书的作者个数 obj = models.Book.objects.annotate(Count('author')).values() #annotate注释 for i in obj: print(i)
#示例2:统计出每个出版社买的最便宜的书的价格 #方式一: obj = models.Publisher.objects.annotate(Min('book__price')).values() for i in obj: print(i) print("*"*30) #方式二: obj = models.Book.objects.values('pub__name').annotate(Min('price')) for i in obj: print(i)
#示例3:统计不止一个作者的图书 obj = models.Book.objects.annotate(count=Count('author')).filter(count__gt=1) print(obj)
#示例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序 obj = models.Book.objects.annotate(count=Count('author')).order_by('count') print(obj)
#示例5:查询各个作者出的书的总价格 obj = models.Author.objects.annotate(Sum('books__price')).values() print(obj)
12.2.7.8F查询 和Q查询
1.F查询
F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
book表字段信息:
#F查询 from django.db.models import F #1.查询售出大于库存的(比较两个字段的值) ret = models.Book.objects.filter(sale__gt=F('kucun')) print(ret)
#更新所有字段 obj = models.Book.objects.get(pk=1) obj.sale=100 obj.save() #只更新sale字段 models.Book.objects.filter(pk=1).update(sale=80) #取某字段的值做相应操作 models.Book.objects.all().update(sale=F('sale')*2+10)
2.Q查询
-
| 或
-
& 与
-
~ 非
from django.db.models import Q ret = models.Book.objects.filter(Q(pk__gt=3)|Q(pk__lt=2)) print(ret) print('*'*30) ret = models.Book.objects.filter(Q(Q(pk__gt=3)|Q(pk__lt=2))&Q(pub__book__kucun__gt=50)) print(ret)
12.2.7.9事务
import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings") import django django.setup() from app01 import models from django.db import transaction try: with transaction.atomic(): #开启事务 #进行一系列ORM操作 models.Publisher.objects.create(name='xxx3') models.Publisher.objects.create(name='xxx4') except Exception as e: print(e)
12.2.8 cookie 和 Session
https://www.cnblogs.com/maple-shaw/articles/9502602.html
12.2.8.1cookie
1.Cookie的由来
-
大家都知道HTTP协议是无状态的。无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。
2.什么叫Cookie
-
定义:保存在浏览器上的一组组键值对
-
特点 :
-
1.是服务器让浏览器进行设置
-
2.浏览器保存在浏览器本地
-
3.下次访问时会自动携带
-
-
能做什么
-
1.登录
-
2.保存浏览习惯
-
3..简单投票
-
判断是否登录
from django.shortcuts import render,redirect,HttpResponse from django.views import View class Login(View): def get(self,request,*args,**kwargs): return render(request,'login.html') def post(self,request,*args,**kwargs): username = request.POST.get('username') pwd = request.POST.get('pwd') if username == 'alex' and pwd == '123': url = request.GET.get('return_url') if url: ret = redirect(url) else: ret = redirect('/index/') #ret.set_cookie('is_login','1') ret.set_signed_cookie('is_login','1','aaa') # key value sale加盐 return ret return render(request,'login.html',{'error':'用户名或密码错误'}) def login_required(func): def inner(request,*args,**kwargs): #is_login = request.COOKIES.get('is_login') is_login = request.get_signed_cookie('is_login','aaa',default='') url = request.path_info if is_login != '1': return redirect('/login/?return_url={}'.format(url)) #已登录 ret = func(request,*args,**kwargs) return ret return inner @login_required def index(request): #获取到cookie is_login = request.COOKIES.get('is_login') if is_login != '1': return redirect('/login/') return HttpResponse('首页') @login_required def home(request): return HttpResponse('home')
-
1.获取Cookie
request.COOKIES['key'] request.get_signed_cookie('key', default=RAISE_ERROR, salt='', max_age=None)
get_signed_cookie方法的参数:
-
default: 默认值
-
salt: 加密盐
-
max_age: 后台控制过期时间
-
-
2.设置Cookie
rep = HttpResponse(...) rep = render(request, ...) rep.set_cookie(key,value,...) rep.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.删除Cookie
def logout(request): ret = redirect("/login/") ret.delete_cookie("is_login") # 删除用户浏览器上之前设置的user的cookie值 return ret
12.2.8.2Session
-
定义:保存在服务器上的一组组键值对,必须依赖与cookie使用
1.为什么要有Session
-
1.cookie保存在浏览器本地
-
2.大小个数受到限制(4096)
2.操作session
-
设置
-
request.session [key] = value
-
-
获取
-
request.session [key]
-
request.session.get(key)
-
3.Session流程解析
4.Django中Session相关方法
# 获取、设置、删除Session中数据 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1'] # 所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 会话session的key request.session.session_key # 将所有Session失效日期小于当前日期的数据删除(expired:过期) request.session.clear_expired() # 检查会话session的key在数据库中是否存在 request.session.exists("session_key") # 删除当前会话的所有Session数据,不删除会话的Cookie request.session.delete() # 删除当前的会话数据,并删除会话的Cookie。 request.session.flush() 这用于确保前面的会话数据不可以再次被用户的浏览器访问 例如,django.contrib.auth.logout() 函数中就会调用它。 # 设置会话Session和Cookie的超时时间 request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。
5.Django中的Session配置
1. 数据库Session SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 2. 缓存Session SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 3. 文件Session SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 4. 缓存+数据库 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 5. 加密Cookie Session SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 其他公用设置项: 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过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
from django.conf import global_settings
12.2.9CSRF
-
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用
隐藏:
12.2.9.1CSRF装饰器
from django.views.decorators.csrf import csrf_exempt,csrf_protect,ensure_csrf_cookie from django.utils.decorators import method_decorator csrf_exempt #某个视图不需要进行csrf校验 csrf_protect #某个视图需要进行csrf校验 ensure_csrf_cookie #确保生成csrf的cookie
12.2.9.2CSRF功能
-
1.crsf中间件中执行process_request方法
-
1.从cookie中获取到csrftoken
-
2.csrftoken中的值放到request.META中
-
-
2.执行process_view方法
-
1.查询视图函数是否使用csrf_exempt装饰器,使用了就不进行csrf校验
-
2.判断请求方式
-
如果是 'GET', 'HEAD', 'OPTIONS', 'TRACE' ,不进行csrf校验
-
其他请求方式进行crsf校验 :
-
获取cookie中的csrftoken中的值
-
获取csrfmiddlewaretoken的值
-
能获取到----->>request_csrf_token
-
获取不到----->>获取请求头中的 X-csrftoken的值----->>request_csrf_token
-
-
-
比较request_csrf_toke和cookief中的csrftoken,比较成功接受请求,比较不成功,拒绝请求
-
-
12.2.10ajax
12.2.10.1.AJAX准备知识:JSON
-
什么是JSON
1. JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) 2. JSON 是轻量级的文本数据交换格式 3. JSON 独立于语言 4. JSON 具有自我描述性,更易理解
不合格的json对象:
{ name: "张三", 'age': 32 } // 属性名必须使用双引号 [32, 64, 128, 0xFFF] // 不能使用十六进制值 { "name": "张三", "age": undefined } // 不能使用undefined { "name": "张三", "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'), "getName": function() {return this.name;} // 不能使用函数和日期对象 }
-
和XML的比较
-
JSON 格式有两个显著的优点:书写简单,一目了然;符合 JavaScript 原生语法,可以由解释引擎直接处理,不用另外添加解析代码。所以,JSON迅速被接受,已经成为各大网站交换数据的标准格式,并被写入ECMAScript 5,成为标准的一部分。XML和JSON都使用结构化方法来标记数据
-
JSON 简单的语法格式和清晰的层次结构明显要比 XML 容易阅读,并且在数据交换方面,由于 JSON 所使用的字符要比 XML 少得多,可以大大得节约传输数据所占用得带宽。
-
12.2.10.2AJAX简介
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
-
同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
-
异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
总结 : ajax就是使用js的技术发请求和接收响应的
特点:1.异步,2.局部刷新,3.传输数据量少
-
发请求的方式
-
地址栏输入地址 GET
-
form请求 GET/POST
-
1.method 请求方式 action 地址
-
2.标签要有name属性,有的要有value
-
3.要有一个input type='submit' 或者button
-
-
a标签 GET
-
ajax
-
使用 : jq发ajax
例一:页面输入两个整数,通过AJAX传输到后端计算出结果并返回
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" name="i1" >+
<input type="text" name="i2" >=
<input type="text" name="i3" >
<button id="b1" >计算</button>
<script src="/static/jquery.js"></script>>
<script>
$("#b1").click(function(){
//发ajax请求
$.ajax({
url:'/calc/',
type:'post',
data:{
a:$("[name='i1']").val(),
b:$("[name='i2']").val()
},
success:function(res){
$("[name='i3']").val(res)
},
error:function(error){
console.log(error)
}
})
})
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse
def calc(request):
a = request.POST.get('a')
b = request.POST.get('b')
c = int(a) + int(b)
return HttpResponse(c)
例二:异步的实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" name="i1" >+
<input type="text" name="i2" >=
<input type="text" name="i3" >
<button id="b1" >计算</button>
<br>
<input type="text" name="ii1" >+
<input type="text" name="ii2" >=
<input type="text" name="ii3" >
<button id="b2">计算</button>
<script src="/static/jquery.js"></script>>
<script>
$("#b1").click(function(){
//发ajax请求
$.ajax({
url:'/calc/',
type:'post',
data:{
a:$("[name='i1']").val(),
b:$("[name='i2']").val()
},
success:function(res){
$("[name='i3']").val(res)
},
error:function(error){
console.log(error)
}
})
});
$("#b2").click(function(){
//发ajax请求
$.ajax({
url:'/calc2/',
type:'post',
data:{
a:$("[name='ii1']").val(),
b:$("[name='ii2']").val()
},
success:function(res){
$("[name='ii3']").val(res)
},
error:function(error){
console.log(error)
}
})
})
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse import time def calc(request): a = request.POST.get('a') b = request.POST.get('b') c = int(a) + int(b) time.sleep(2) return HttpResponse(c) def calc2(request): a = request.POST.get('a') b = request.POST.get('b') c = int(a) + int(b) return HttpResponse(c)
例三:列表元素的获取(法一)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <button id="b3">参数</button> <script src="/static/jquery.js"></script>> <script> $("#b3").click(function(){ //发ajax请求 $.ajax({ url:'/test/', type:'post', data:{ name: 'alex', age:30, hobby:['唱','跳','rap'], }, success:function(res){ console.log(res) }, error:function(error){ console.log(error) } }) }) </script> </body> </html>
from django.shortcuts import render,HttpResponse def test(request): print(request.POST) ret = request.POST.get('hobby') print(ret) ret = request.POST.get('hobby[]') print(ret) ret = request.POST.getlist('hobby[]') print(ret) return HttpResponse('OK')
例四:列表元素的获取(法二)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <button id="b3">参数</button> <script src="/static/jquery.js"></script>> <script> $("#b3").click(function(){ //发ajax请求 $.ajax({ url:'/test/', type:'post', data:{ name: 'alex', age:30, hobby:JSON.stringify(['唱','跳','rap']), }, success:function(res){ console.log(res) }, error:function(error){ console.log(error) } }) }) </script> </body> </html>
from django.shortcuts import render,HttpResponse import json def test(request): print(request.POST) ret = request.POST.get('hobby') print(ret) obj = json.loads(ret) print(obj,type(obj)) return HttpResponse('OK')
例五:上传文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="file" id="f1"> <button id="b1">上传</button> <script src="/static/jquery.js"></script>> <script> $("#b1").click(function(){ var formobj = new FormData(); formobj.append('file',document.getElementById('f1').files[0]); formobj.append('name','alex'); //发ajax请求 $.ajax({ url:'/upload/', type:'post', data:formobj, processData:false, #不处理编码方式 contentType:false, # success:function(res){ $("[name='i3']").val(res) }, error:function(error){ console.log(error) } }) }) </script> </body> </html>
from django.shortcuts import render,HttpResponse def upload(request): print(request.POST) print(request.FILES) return render(request,'upload.html')
例六:ajax通过csrf校验
-
1.确保有csrftoken的cookie
在页面中使用{% csrf_token%}
-
2.加装饰器ensure_csrf_cookie
from django.views.decorators.csrf import csrf_exempt,csrf_protect,ensure_csrf_cookie
-
1.给data中添加crsfmiddlewaretoken键值对
data:{ 'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val(), a:$("[name='i1']").val(), b:$("[name='i2']").val() },
2.加请求头
header:{ 'x-csrftoken':$('[name="csrfmiddlewaretoken"]').val(), },
3.使用文件(static/js)
function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } });
-
12.2.11简单的登录的实例
<body> <div class="container"> <form class="form-signin" action="" method="post"> <h2 class="form-signin-heading">Please sign in</h2> <label for="inputEmail" class="sr-only">Email address</label> <input type="text" id="inputEmail" class="form-control" placeholder="Email address" required="" autofocus="" name="username"> <label for="inputPassword" class="sr-only">Password</label> <input type="password" id="inputPassword" class="form-control" placeholder="Password" name="passward"> <div class="checkbox"> <label> <input type="checkbox" value="remember-me"> Remember me </label> </div> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> </form> </div> <!-- /container --> </body>
form表单提交数据注意的问题:
-
1.提交的地址action="",请求的方式 method="post"
-
2.所有input框都有name属性
-
3.有一个input框的type="submit" 或者 有一个button
运行程序会出现以下情况
提交POST请求,把settings中MIDDLEWARE的'django.middleware.csrf.CsrfViewMiddleware'注释掉
from django.conf.urls import url from django.contrib import admin from django.shortcuts import render def index(request): return render(request,"index.html") def login(request): print(request.method,type(request.method)) return render(request,"login.html") urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', index), url(r'^login/', login), ]
点击路径回车,会得到
输入账号密码提交,会得到
from django.conf.urls import url from django.contrib import admin from django.shortcuts import render def index(request): return render(request,"index.html") def login(request): #print(request.method,type(request.method)) if request.method == 'GET': return render(request,"login.html") elif request.method == 'POST': #获取form表单提交的数据 print(request.POST) #然后验证用户名和密码 #验证成功,跳转到index页面 #验证不成功,重新登陆 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', index), url(r'^login/', login), ]
from django.conf.urls import url from django.contrib import admin from django.shortcuts import render def index(request): return render(request,"index.html") def login(request): #print(request.method,type(request.method)) if request.method == 'POST': #获取form表单提交的数据 print(request.POST) username = request.POST['username'] print(username,type(username)) #然后验证用户名和密码 #验证成功,跳转到index页面 #验证不成功,重新登陆 return render(request,"login.html") urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', index), url(r'^login/', login), ]
from django.conf.urls import url from django.contrib import admin from django.shortcuts import render,redirect def index(request): return render(request,"index.html") def login(request): #print(request.method,type(request.method)) if request.method == 'POST': #获取form表单提交的数据 print(request.POST) username = request.POST['username'] password = request.POST['password'] print(username,type(username)) #然后验证用户名和密码 if username == 'alex' and password == 'sb': #验证成功,跳转到index页面 return redirect('https://www.baidu.com/') #验证不成功,重新登陆 return render(request,"login.html") urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', index), url(r'^login/', login), ]
# request.method #请求方式 POST GET # request.POST #form表单提交的数据,去数据方式 [] 或者 .get .get好处,有默认值 from django.shortcuts import redirect return redirect('https://www.baidu.com/')
12.2.12图书管理系统
12.2.12.1出版社管理
-
1.创建项目
-
2.创建mysql数据库
-
3.更改配置
在init中添加代码
-
4.建表
class publisher(models.Model): #默认生成id #设置主键 pid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) addr = models.CharField(max_length=32)
在终端里做数据库迁移命令
-
1.展示
设计url
from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), #添加出版社表信息路径 url(r'^publisher_list/', views.publisher_list), ]
写函数
from django.shortcuts import render from app01 import models #展示出版社 def publisher_list(request): #从数据库中查询出版社信息 all_publisher = models.publisher.objects.all() #返回一个包含出版社信息的页面 return render(request,'publisher_list.html',{'all_publisher':all_publisher})
写模板
<body> <h1>出版社列表</h1> {% comment %}{{all_publisher}}{% endcomment %} <table border="1"> <thead> <tr> <th>序号</th> <th>id</th> <th>名称</th> </tr> </thead> <tbody> {% for publisher in all_publisher %} <tr> <td>{{forloop.counter}}</td> <td>{{publisher.pk}}</td> <td>{{publisher.name}}</td> </tr> {% endfor%} </tbody> </table> </body> </html>
-
2.新增
#基础 from django.shortcuts import render,redirect from app01 import models #展示出版社 def publisher_list(request): #从数据库中查询出版社信息 all_publisher = models.publisher.objects.all() #返回一个包含出版社信息的页面 return render(request,'publisher_list.html',{'all_publisher':all_publisher}) #新增出版社 def add_publisher(request): #对请求方式进行判断 if request.method == 'POST': #处理POST请求 #获取出版社名称 publisher_name = request.POST.get('publisher_name') #判断出版社名称是否重复 if models.publisher.objects.filter(name=publisher_name): return render(request, 'add_publisher.html',{'error':"出版社名称已存在"}) #判断输入值是否为空 if not publisher_name: return render(request, 'add_publisher.html', {'error': "输入不能为空"}) #使用orm将数据插入到数据库中 obj = models.publisher.objects.create(name=publisher_name) print(obj.pk,obj.name) #跳转到展示出版社界面 return redirect('/publisher_list/') #返回一个包含form表单的页面 return render(request,'add_publisher.html')
#优化版 from django.shortcuts import render,redirect from app01 import models #展示出版社 def publisher_list(request): #从数据库中查询出版社信息 all_publisher = models.publisher.objects.all().order_by("pk") #返回一个包含出版社信息的页面 return render(request,'publisher_list.html',{'all_publisher':all_publisher}) #新增出版社 def add_publisher(request): #对请求方式进行判断 error = '' if request.method == 'POST': #处理POST请求 #获取出版社名称 publisher_name = request.POST.get('publisher_name') #判断出版社名称是否重复 if models.publisher.objects.filter(name=publisher_name): error = "出版社名称已存在" #判断输入值是否为空 if not publisher_name: error = "输入不能为空" if not error: #使用orm将数据插入到数据库中 obj = models.publisher.objects.create(name=publisher_name) #print(obj.pk,obj.name) #跳转到展示出版社界面 return redirect('/publisher_list/') #返回一个包含form表单的页面 return render(request,'add_publisher.html',{'error':error})
-
3.删除
obj_list = models.Publisher.objects.filter(pk=pk) obj_list.delete() obj = models.Publisher.objects.get(pk=pk) obj.delete()
#删除出版社 def del_publisher(request): #获取要删除的去数据 pk = request.GET.get('id') obj_list = models.publisher.objects.filter(pk=pk) if not obj_list: #没有要删除的数据(直接输入) return HttpResponse('要删除的数据不存在') #删除该数据 #obj.delete() obj_list.delete() #跳转到展示页面 return redirect('/publisher_list/')
-
4.编辑
#编辑出版社 def edit_publisher(request): error = '' #查找要编辑的页面 pk = request.GET.get('id') obj_list = models.publisher.objects.filter(pk=pk) if not obj_list: return HttpResponse('要编辑的数据不存在') obj = obj_list[0] if request.method == 'POST': #处理POST请求 #获取新提交的出版社名称 publisher_name = request.POST.get('publisher_name') if models.publisher.objects.filter(name=publisher_name): #新修改的名称已存在 error = '新修改的名称已存在' if obj.name == publisher_name: error = '名称未修改' if not publisher_name: error = '提交名称不能为空' if not error: #修改数据 obj.name = publisher_name #保存数据到数据库 obj.save() #跳转到出版社展示页面 return redirect('/publisher_list/') #返回一个包含原始数据的页面 return render(request,'edit_publisher.html',{'obj':obj,'error':error})
12.2.12.2书籍管理
-
1.结构
class Book(models.Model): name = models.CharField(max_length=32,unique=True) pub_id = models.ForeignKey('publishe',on_delete=models.CASCADE) #models.CASCADE 级联删除 #on_delete参数 #1.11版本之前不用填写.默认是on_delete=models.CASCADE),但在2.0版本之后必填 #on_delete=models.SET(value)设置成某个值 SET_DEFAULT 设置成默认值 SET_NULL
执行数据库迁移命令
-
#展示书籍 def book_list(request): #获取所有书籍对象 books = models.Book.objects.all() for i in books: i,type(i) i.pk, type(i.pk) i.name, type(i.name) i.pub, type(i.pub) #所关联对象 i.pub.pk #所关联对象id i.pub_id #所关联对象id(推荐使用)
#新增书籍
def add_book(request):
if request.method == 'POST':
#获取提交数据
new_name = request.POST.get('new_name')
pub_id = request.POST.get('pub')
#插入到数据库中
models.Book.objects.create(name=new_name,pub=models.publisher.objects.get(pk=pub_id))
#跳转到展示页面
return redirect('/book_list/')
#所有的出版社信息
publisher = models.publisher.objects.all()
return render(request,'add_book.html',{'publisher':publisher})#编辑书籍
def edit_book(request):
#查询要编辑书籍的对象
pk=request.GET.get('pk')
edit_obj=models.Book.objects.get(pk=pk)
if request.method == 'POST':
#获取提交数据
new_name=request.POST.get('new_name')
pub_id = request.POST.get('pub')
#编辑数据
edit_obj.name=new_name
edit_obj.pub_id = pub_id #id
#edit_obj.pub= models.publisher.objects.get(pub=pub_id) 对象
#保存到数据库
edit_obj.save()
#重定向到展示界面
return redirect('/book_list/')
#查询所有出版社
publisher = models.publisher.objects.all()
return render(request,'edit_book.html',{'edit_obj':edit_obj,'publisher':publisher})
12.2.12.3作者表
-
建表
class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField('Book')
-
展示作者
设计url
url(r'^author_list/', views.author_list),
写函数
#展示作者 def author_list(request): #查询所有作者 all_author = models.Author.objects.all() for author in all_author: print(author) print(author.pk) print(author.name,type(author.name)) print(author.books,type(author.books)) #关系管理对象 print(author.books.all(), type(author.books.all())) print("*"*20) return render(request,'author_list.html',{'all_author':all_author})
写模板
<body> <h1>作者列表</h1> <table border="1"> <thead> <tr> <th>序号</th> <th>作者id</th> <th>作者姓名</th> <th>代表作</th> </tr> </thead> <tbody> {% for author in all_author %} <tr> <td>{{ forloop.counter }}</td> <td>{{ author.pk }}</td> <td>{{ author.name }}</td> <td> {% for book in author.books.all %} {% if forloop.last %} <<{{ book.name }}>> {% else %} <<{{ book.name }}>>, {% endif %} {% endfor %} </td> </tr> {% endfor %} </tbody> </table> </body>
-
增加
class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField('Book')
写函数
#增加作者 def add_author(request): if request.method == 'POST': #获取post请求提交数据 author_name = request.POST.get('author_name') books = request.POST.get('books') """ print(request.POST) print(author_name) print(books) """ #存到数据库 author_obj = models.Author.objects.create(name=author_name ) #只插入book表中内容 author_obj.books.set(books) #设置作者和书籍多对对关系 #跳转到展示界面 return redirect('/author_list/') #查询所有书籍 all_books = models.Book.objects.all() return render(request,'add_author.html',{"all_books":all_books})
写模板
<body> <form action="" method="POST"> <p>作者姓名:<input type="text" name="author_name"></p> <p>作品: <select name="books" id="" multiple> <option value=""></option> {% for book in all_books %} <option value="{{ book.pk }}">{{ book.name }}</option> {% endfor %} </select> </p> <button>提交</button> </form> </body>
-
编辑
class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField('Book')
写对象
#编辑作者 def edit_author(request): #查询要编辑作者对象 pk = request.GET.get('pk') author_obj = models.Author.objects.get(pk=pk) if request.method == 'POST': #获取提交的数据 name = request.POST.get('author_name') books = request.POST.getlist('books') #修改对象的数据 author_obj.name = name author_obj.save() #多对多的关系 author_obj.books.set(books) #每次重新设置 #重定向 return redirect('/author_list/') #查询所有书籍 all_book = models.Book.objects.all() return render(request,'edit_author.html',{'author_obj':author_obj,'all_book':all_book})
<body> <form action="" method="post"> <p>作者姓名:<input type="text" name="author_name" value="{{ author_obj.name }}"></p> <p>作品: <select name="books" id="" multiple> {% for book in all_book %} {% if book in author_obj.books.all %} <option selected value="{{ book.pk }}">{{ book.name }}</option> {% else %} <option value="{{ book.pk }}">{{ book.name }}</option> {% endif %} {% endfor %} </select> </p> <button>提交</button> </form> </body>
12.2.12.4django设置多对多关系三种方法
-
1.django帮我们生成第三张表
class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField('Book') # 不在Author表中生产字段,生产第三张表
-
2.自己创建第三张表
class AuthorBook(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCADE) book = models.ForeignKey(Book, on_delete=models.CASCADE) #可任意添加字段 date = models.DateField()
-
3.自建的表和 ManyToManyField 联合使用
class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField('Book',through='AuthorBook',through_fields=['author','book']) # 不在Author表中生产字段,生产第三张表 class AuthorBook(models.Model): author = models.ForeignKey(Author,related_name='a', on_delete=models.CASCADE) book = models.ForeignKey(Book,related_name='b', on_delete=models.CASCADE) tuijian = models.ForeignKey(Author, on_delete=models.CASCADE) date = models.DateField()
12.2.12.5
12.2.12.5升级版
def delete(request,table,pk): print(table,pk) #查找对应对象 table_class = getattr(models,table.capitalize()) table_class.objects.filter(pk=pk).delete() #删除 #重定向到对应展示界面 return redirect(reverse(table))
12.2.13 form组件
https://www.cnblogs.com/maple-shaw/articles/9537309.html
12.2.12.1 form组件的功能
# 1.生成input标签 #2.对提供的数据进行校验 #3.提供错误提示
-
1.定义form组件
from django import forms class Regform(forms.Form): username = forms.CharField(label='用户名') #label 改为中文 pwd = forms.CharField(label='密码',widget=forms.PasswordInput) #widget 密文
-
2.使用
视图里
def reg2(request): form_obj = Regform() if request.method == 'POST': form_obj = Regform(data=request.POST) if form_obj.is_valid(): #对数据进行校验 #插入数据 return HttpResponse('注册成功') return render(request,'reg2.html',{'form_obj':form_obj})
模板里
<form action="" method="post" novalidate> {# novalidate:不在前端进行校验 #} {% csrf_token %} {{ form_obj.as_p }} # #生成一个个p标签(包含input label) {{ form_obj.errors }} <button>注册</button> </form>
{{ form_obj.as_p }} # 生成一个个p标签(包含input label) {{ form_obj.errors }} #生成所有错误 {{ form_obj.username }} #一个字段所对应的input框 {{ form_obj.username.label }} #该字段的中文提示 {{ form_obj.username.id_for_label }} #该字段input的id {{ form_obj.username.errors }} #该字段的所有错误 {{ form_obj.username.errors.0 }} #该字段的第一个错误
12.2.12.2常用字段与插件
-
1.initial
初始值,input框里面的初始值
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三" # 设置默认值 ) pwd = forms.CharField(min_length=6, label="密码")
-
2.error_messages
错误提示
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", required=True, error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ) pwd = forms.CharField(min_length=6, label="密码")
-
3.password
密文显示
class LoginForm(forms.Form): pwd = forms.CharField(label='密码',widget=forms.PasswordInput) #widget 密文
-
4. radioSelect
class LoginForm(forms.Form): gender = forms.fields.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.RadioSelect )
-
5.单选Select
class LoginForm(forms.Form): ... hobby = forms.fields.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=3, widget=forms.widgets.Select() )
-
6.多选Select
lass LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=[1, 3], widget=forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),) )
-
7.单选checkbox
class LoginForm(forms.Form): ... keep = forms.fields.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() )
-
8.多选checkbox
class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
12.2.12.3Django Form所有内置字段
Field
required=True, 是否允许为空
widget=None, HTML插件
label=None, 用于生成Label标签或显示内容
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}
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,