第十三章: Django 框架
1. HTTP认识
http工作原理
HTTP 请求/响应的步骤:
-
客户端连接到Web服务器
一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如,http://www.luffycity.com。
-
发送HTTP请求
通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
-
服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
-
释放连接TCP连接
若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;
-
客户端浏览器解析HTML内容
客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
http请求方法
HTTP/1.1协议中共定义了八种方法(也叫“动作”)来以不同方式操作指定的资源:
-
get:
- 向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,例如在Web Application中。其中一个原因是GET可能会被网络蜘蛛等随意访问。
-
head:
- 与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)。
-
post:
-
向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。
<form action="http://127.0.0.1:8001" method="post"> 默认是get请求 <input type="text" name="user"> <input type="password" name="pwd"> <input type="submit"> </form>
-
-
put:
- 向指定资源位置上传其最新内容。
-
delete:
- 请求服务器删除Request-URI所标识的资源
-
trace:
- 回显服务器收到的请求,主要用于测试或诊断。
-
options:
- 这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。
-
connect:
- HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)
http状态码
所有HTTP响应的第一行都是状态行,依次是当前HTTP版本号,3位数字组成的状态代码,以及描述状态的短语,彼此由空格分隔
状态代码的第一个数字代表当前响应的类型:
- 1xx消息——请求已被服务器接收,继续处理
- 2xx成功——请求已成功被服务器接收、理解、并接受
- 3xx重定向——需要后续操作才能完成这一请求
- 4xx请求错误——请求含有词法错误或者无法被执行
- 5xx服务器错误——服务器在处理某个正确请求时发生错误
URL
超文本传输协议(HTTP)的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中:
-
传送协议。
-
层级URL标记符号(为[//],固定不变)
-
访问资源需要的凭证信息(可省略)
-
服务器。(通常为域名,有时为IP地址)
-
端口号。(以数字方式表示,若为HTTP的默认值“:80”可省略)
-
路径。(以“/”字符区别路径中的每一个目录名称)
-
查询。(GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
-
片段。以“#”字符为起点
以http://www.luffycity.com:80/news/index.html?id=250&page=1 为例, 其中: http,是协议; www.luffycity.com,是服务器; 80,是服务器上的默认网络端口号,默认不显示; /news/index.html,是路径(URI:直接定位到对应的资源); ?id=250&page=1,是查询
HTTP请求格式(请求协议)
HTTP响应格式(响应协议)
2. WEB框架本质
多线程的web框架
import socket
from threading import Thread
sk = socket.socket()
sk.bind(('127.0.0.1',8008))
sk.listen()
def html(conn):
with open('demo2.html', 'rb') as f:
data = f.read()
conn.send(data)
conn.close()
def css(conn):
with open('test.css', 'rb') as f:
data = f.read()
conn.send(data)
conn.close()
def js(conn):
with open('test.js', 'rb') as f:
data = f.read()
conn.send(data)
conn.close()
def jpg(conn):
with open('1.png', 'rb') as f:
data = f.read()
conn.send(data)
conn.close()
def ico(conn):
with open('jd.ico', 'rb') as f:
data = f.read()
conn.send(data)
conn.close()
urlpatterns = [
('/',html),('/test.css',css),
('/1.png',jpg),('/test.js',js),('/jd.ico',ico),]
while True:
conn,addr = sk.accept()
mes = conn.recv(1024).decode('utf-8')
path = mes.split('
')[0].split(' ')[1]
print(path)
conn.send(b'http/1.1 200 ok
')
for i in urlpatterns:
if path == i[0]:
t = Thread(target=i[1],args=(conn,))
t.start()
# <link rel="icon" href="jd.ico"> 在head标签中设置ico,(小图标)
设置动态的web框架
# 在上面的基础上修改
def html(conn):
time_tag = str(time.time())
with open('demo2.html', 'r',encoding='utf-8') as f:
data = f.read()
# html中随便添加一个内容,然后将其替换
data = data.replace('xxoo',time_tag).encode('utf-8')
conn.send(data)
conn.close()
wsgired 模块版的web框架
-
wsgiref本身就是个web框架,提供了一些固定的功能(请求和响应信息的封装,不需要我们自己写原生的socket了也不需要咱们自己来完成请求信息的提取了,提取起来很方便
-
environ : 是全部加工好的请求信息,加工成了一个字典,通过字典取值的方式就能拿到很多你想要拿到的信息
-
start_response: 帮你封装响应信息的(响应行和响应头),注意下面的参数
import time from wsgiref.simple_server import make_server def html(): time_tag = str(time.time()) with open('demo2.html', 'r',encoding='utf-8') as f: data = f.read() data = data.replace('xxoo',time_tag).encode('utf-8') return data def css(): with open('rr.css', 'rb') as f: data = f.read() return data def js(): with open('test.js', 'rb') as f: data = f.read() return data def jpg(): with open('1.jpg', 'rb') as f: data = f.read() return data def ico(): with open('jd.ico', 'rb') as f: data = f.read() return data urlpatterns = [ ('/',html),('/rr.css',css), ('/1.jpg',jpg),('/test.js',js),('/jd.ico',ico),] def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html'),('k1','v1')]) path = environ['PATH_INFO'] # 拿到每个请求 print(path) for i in urlpatterns: if path == i[0]: ret = i[1]() break else: ret = b'404 not found!!!!' return [ret] httpd = make_server('127.0.0.1', 8080, application) print('Serving HTTP on port 8080...') httpd.serve_forever()
jinja2 版的框架
# HTML文件
<h1>{{userinfo}}</h1>
<ul>
{% for k,v in userinfo.items()%}
<li>{{k}}--{{v}}</li>
{% endfor %}
</ul>
# py文件 在上面基础上修改
from jinja2 import Template
def html():
user = showdata()
with open('demo2.html', 'r',encoding='utf-8') as f:
data = f.read()
tem = Template(data)
data = tem.render({'userinfo':user})
data = data.encode('utf-8')
return data
3. MVC 和 MTV框架
-
所谓的mvc就是把web应用分为模型(M),控制器(C),和视图(V),他们之间以一种插件式的,松耦合的方式连接在一起的,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互,控制器接收用户的输入调用模型和视图完成用户的请求
-
Django的MTV模式本质上和mvc是一样的,也是为了各组件保持松耦合的关系,只是定义上有些不同,Django的MTV分别是值
- M : 代表模型(Model),负责业务数据和数据库的映射
- T : 代表模板(Template),负责如何把页面展示给用户(HTML)
- V : 代表视图(View),负责业务逻辑,并在适当的时候调用Model和Template.
除了以上三层外,还需要一个URL分发器,它的作用是将一个URL的页面请求分发给不同的View处理,View在调用相应的Model和Template,响应模式如下
- 一般是用户通过浏览器向我们的服务器发起一个请求(request),这个请求回去访问视图函数,(如果不涉及到数据调用,那么这个时候视图函数返回一个模板也就是一个网页给用户),视图函数调用模型,模型去数据库查找数据,然后逐级返回,视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。
4. Django下载安装
- 官网地址 :
https://www.djangoproject.com/download/
- 下载Django: pip3 install django == 1.11.9
- 创建一个Django project:
- Django-admin startproject mysite 创建一个名为mysite的Django项目
- 创建app : python manage.py startapp app的名字
- 创建的项目的文件介绍
- manage.py -- Django项目里面的工具,通过他可以调用Django shell和数据库,启动关闭项目与项目交互 等,不管你将框架分了几个文件,必然有一个启动文件,其实他们本身就是一个文件。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
- urls.py ----- 负责把URL模式映射到应用程序。
- wsgi.py ---- runserver命令就使用wsgiref模块做简单的web server,后面会看到renserver命令,所有与 socket相关的内容都在这个文件里面了,目前不需要关注它。
- 运行项目
- python manage.py runserver 127.0.0.1:8080 #此时已经可以启动django项目了,只不过什么逻辑也没有
- 通过指令运行项目的时候,可以不写ip地址,如果不写,默认是127.0.0.1
- python manage.py runserver 8080
- 如果连端口都不写,默认是8000端口
- python manage.py runserver
5. 写项目
1 创建项目 first_pro
2 创建app app01
做一个登录页面的web项目,浏览器输入一个网址得到一个web页面
用户: http:127.0.0.1:8001/login/
1 urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [ # url(r'^admin/', admin.site.urls),
url(r'^index/', views.index), #配置路径,r'^/index/',这个前置导航斜杠不需要添加.
]
# urls.py里面需要注意的问题
url(r'^index/', views.index), 第一个参数:路径正则字符串 第二个参数:对应的视图函数
2 写逻辑视图(Views)
from django.shortcuts import render,HttpResponse
# Create your views here.
def index(request):
print(request.method) # 'POST' 'GET'
if request.method == 'GET':
return render(request,'login.html')
else:
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'xin' and password == '123':
return HttpResponse('登录成功!')
else:
return HttpResponse('登录失败!')
3 创建html文件(Template文件下)
在templates文件夹中创建一个login.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>Bootstrap 101 Template</title>
</head>
<body>
<form action="/login/" method="post">
姓名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
4. views.py
from django.shortcuts import render,HttpResponse
# Create your views here.
def login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
name = request.POST.get('username')
ped = request.POST.get('password')
if name== 'xin' and ped == '666':
return HttpResponse('成功')
else:
return HttpResponse('失败')
6. URL路由系统
6.1 URL配置
-
基本格式
from django.conf.urls import url #循环urlpatterns,找到对应的函数执行,匹配上一个路径就找到对应的函数执行,就不再往下循环了,并给函数传一个参数request,和wsgiref的environ类似,就是请求信息的所有内容 urlpatterns = [ url(正则表达式, views视图函数,参数,别名), ]
-
参数说明
- 正则表达式: 一个正则表达式字符串
- views视图函数: 一个可调用对象,通常为一个视图函数或一个指定函数路径的字符串
- 参数: 可选的要传给视图函数的默认参数(字典形式)
- 别名: 一个可选的name参数
6.2 正则表达式详解
-
基本配置
urls.py: from django.conf.urls import url from django.contrib import admin from app02 import views urlpatterns = [ url(r'^books/(d{4})/$', views.books), # 匹配年 url(r'^books/(d{4})/(d{1,2})/', views.books), # 匹配年月 ] views.py: # 第一个参数必须是request,后面跟的三个参数是对应着上面分组正则匹配的每个参数的 def books(request,year,month): return HttpResponse(year+month)
-
补充说明:
# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项 APPEND_SLASH=True
6.3 分组命名匹配
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^articles/(d{4})/$', views.year_archive), # year_archive(request,n),小括号为分组,有分组,那么这个分组得到的用户输入的内容,就会作为对应函数的位置参数传进去,别忘了形参要写两个了
url(r'^articles/(?P<year>d{4})/$', views.year_archive),#某年的,(?P<year>[0-9]{4})这是命名参数,那么函数year_archive(request,year),形参名称必须是year这个名字。而且注意如果你这个正则后面没有写$符号,即便是输入了月份路径,也会被它拦截下拉,因为它的正则也能匹配上
url(r'^articles/(?P<year>d{4})/(?P<month>d{2})/$', views.month_archive),#某年某月的
url(r'^articles/(?P<year>d{4})/(?P<month>d{2})/(?P<day>d{2})/$', views.article_detail), #某年某月某日的
]
-
视图函数中指定默认值
# urls.py中 from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^blog/$', views.page), url(r'^blog/page(?P<num>d+)/$', views.page), ] # views.py中,可以为num指定默认值 def page(request, num="1"): pass
6.4 URL路由分发 -- include
# 项目文件夹下的urls.py文件中的url写法:
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
urlpatterns = [
#首页
url(r'^$', views.base),
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
]
# app01下urls.py内容写法
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^$', views.app01base),
]
# app02下urls.py内容写法
from django.conf.urls import url
from django.contrib import admin
from app02 import views
urlpatterns = [
url(r'^$', views.app02base),
]
7. 视图函数
7.1 请求相关的属性方法(request-->HttpRequest对象)
def index(request): # http相关请求信息---封装--HttpRequest对象
if request.method == 'GET':
print(request.body) # 获取post请求提交过来的原始数据
print(request.GET) # 获取GET请求提交的数据
# print(request.META) # 请求头相关信息,就是一个大字典
print(request.path) # /index/ 路径
print(request.path_info) # /index/ 路径
print(request.get_full_path()) # /index/?username=dazhuang&password=123
return render(request,'index.html')
else:
print(request.body) # b'username=dazhuang'
print(request.POST) # 获取POST请求提交的数据
return HttpResponse('男宾三位,拿好手牌!')
7.2 响应相关方法
HttpResponse --- 回复字符串的时候来使用
render --- 回复一个html页面的时候使用
redirect -- 重定向
示例:
# views.py
from django.shortcuts import render,HttpResponse,redirect
def login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'taibai' and password == 'dsb':
return redirect('/home/') # 重定向
else:
return HttpResponse('滚犊子,赶紧去充钱!!!')
def home(request):
return render(request,'home.html')
# urls.py
urlpatterns = [
url(r'^test/', views.test),
url(r'^home/', views.home),
]
7.3 301和302的区别
1)301和302的区别。
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取
(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。
他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;
302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 SEO302好于301
2)重定向原因:
(1)网站调整(如改变网页目录结构);
(2)网页被移到一个新地址;
(3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)。
这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的
网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。
临时重定向(响应状态码:302)和永久重定向(响应状态码:301)对普通用户来说是没什么区别的,它主要面向的是搜索引擎的机器人。
- A页面临时重定向到B页面,那搜索引擎收录的就是A页面。
- A页面永久重定向到B页面,那搜索引擎收录的就是B页面。
7.4 CBV和FBV
-
FBV(function based views) : 就是在视图里使用函数处理
def home(request): print('home!!!') return render(request,'home.html')
-
CBV(class based views) : 在视图里使用类处理请求
views.py from django.views import View class LoginView(View): # 通过请求方法找到自己写的视图类里面对应的方法 def get(self,request): return render(request,'login2.html') def post(self,request): username = request.POST.get('uname') password = request.POST.get('pwd') print(username,password) return HttpResponse('登录成功!') urls.py url(r'^login2/', views.LoginView.as_view()),
-
FBV加装饰器
def wrapper(f): def inner(*args,**kwargs): print('请求之前') ret = f(*args,**kwargs) print('请求之后') return ret return inner @wrapper def home(request): print('home!!!') return render(request,'home.html')
-
CBV加装饰器
from django.views import View from django.utils.decorators import method_decorator def wrapper(f): def inner(*args,**kwargs): print('请求之前') ret = f(*args,**kwargs) print('请求之后') return ret return inner @method_decorator(n1,name='get') # 方式三 class LoginView(View): @method_decorator(n1) # 方式2 给所有方法加装饰器 def dispatch(self, request, *args, **kwargs): # print('请求来啦') ret = super().dispatch(request, *args, **kwargs) # print('到点了,走人了') return ret @method_decorator(n1) # 方式1 def get(self,request): print('get方法执行了') return render(request,'login2.html') def post(self,request): username = request.POST.get('uname') password = request.POST.get('pwd') return HttpResponse('登录成功!')
-
CBV的dispatch方法
from django.views import View class LoginView(View): # GET def dispatch(self, request, *args, **kwargs): print('请求来啦') ret = super().dispatch(request, *args, **kwargs) print('到点了,走人了') return ret def get(self,request): print('get方法执行了') return render(request,'login2.html') def post(self,request): username = request.POST.get('uname') password = request.POST.get('pwd') print(username,password) return HttpResponse('登录成功!')
8. 模板系统
8.1 语法
- 变量相关的用: {{ 变量名 }}
- 逻辑相关的用: {% 逻辑 %}
8.2 变量
# 示例
html代码:
<p>{{ num }}</p>
<p>{{ name }}</p>
<p>{{ name_list.2 }}</p> # 通过 .索引 来取值
<p>{{ d1.age }}</p> # 通过 .键 取值
<p>{{ a.kind }}</p>
<p>{{ a.eat }}</p> # 调用类中方法
views.py代码
def index(request):
num = 100
name = 'shige'
name_list = ['大壮','小壮','壮壮']
d1 = {'name':'大壮','age':73,'hobby':'xuefei+xiangxi'}
class Animal:
def __init__(self):
self.kind = 'dog'
def eat(self):
return 'shi'
a = Animal()
return render(request,'index.html',{'num':num,'name':name,'namelist':name_list,'d1':d1,'a':a})
# return render(request,'index.html',locals())
locals() 获取函数内部所有变量的值,并加工成{'变量名':'变量值'....}这样一个字典
8.3 过滤器
-
default : 如果一个变量是false或者为空,使用给定的默认值.否则使用变量的值
{{ value|default:"nothing"}}
-
length : 返回值的长度,作用于字符串和列表。
mes = 'I love you three thousand times' {{ mes|length }} # 31
-
filesizeformat : 将值格式化为一个 “人类可读的” 文件尺寸 (例如
'13 KB'
,'4.1 MB'
,'102 bytes'
, 等等)filesize = 234352452 {{ filesize|filesizeformat }} # 223.5 MB
-
slice : 切片,如果 value="hello world",还有其他可切片的数据类型
{{value|slice:"2:-1"}}
-
date : 格式化,如果 value=datetime.now()
{{ value|date:"Y-m-d H:i:s"}}
-
safe : 将字符串识别成标签
val = '<a href="http://www.baidu.com">百度</a>' ret = "<script>alert('123')</script>" {{ val|safe }} # 生成a标签的样式 {{ ret|safe }} # 有js的样式
-
truncatechars : 如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
mes = 'I love you three thousand times' {{ value|truncatechars:10}} #注意:最后那三个省略号也是9个字符里面的,也就是这个9截断出来的是6个字符+3个省略号,得到结果 : I love ... (空格一算一个字符)
-
truncatewords : 在一定数量的字后截断字符串,是截多少个单词。
mes = 'I love you three thousand times' {{ value|truncatewords:3}} # I love you ...
-
cut : 移除所有的与给出变量相同的字符串
mes = 'I love you three thousand times' {{ value|cut:' ' }} # Iloveyouthreethousandtimes
-
join : 使用字符串连接列表,类似于python中str.join(list)
lis = [11,22,33] {{ lis|join:'-' }} # 11-22-33
8.4 标签Tags
-
for标签
-
遍历每一个元素: 写个for,然后 tab键自动生成for循环的结构
dic = {'name':'alex','age':77} {% for k,v in dic.items %} # 循环字典 <li>{{ k }} - {{ v }}</li> {% endfor %} # name - alex # age - 77
-
for循环的其他方法
forloop.counter 当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能 forloop.counter0 当前循环的索引值(从0开始) forloop.revcounter 当前循环的倒序索引值(从1开始) forloop.revcounter0 当前循环的倒序索引值(从0开始) forloop.first 当前循环是不是第一次循环(布尔值) forloop.last 当前循环是不是最后一次循环(布尔值) forloop.parentloop 本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等 forloop.parentloop.counter
{# {% for key,value in d1.items %}#} {# {{ forloop.counter }}#} {# <li>{{ key }} -- {{ value }}</li>#} {# {% endfor %}#} {# {% for key,value in d1.items %}#} {# {{ forloop.counter0 }}#} {# <li>{{ key }} -- {{ value }}</li>#} {# {% endfor %}#} {# {% for key,value in d1.items %}#} {# {{ forloop.revcounter }}#} {# <li>{{ key }} -- {{ value }}</li>#} {# {% endfor %}#} {# {% for key,value in d1.items %}#} {# {{ forloop.revcounter0 }}#} {# <li>{{ key }} -- {{ value }}</li>#} {# {% endfor %}#} {# {% for key,value in d1.items %}#} {# {{ forloop.first }}#} {# <li>{{ key }} -- {{ value }}</li>#} {# {% endfor %}#} <!-- forloop.parentloop示例 --> {#<ul>#} {# {% for dd2 in d2 %}#} {# <li>#} {# {% for ddd2 in dd2 %}#} {# {{ forloop.parentloop.counter }}#} {# {{ forloop.counter }}#} {# <a href="">{{ ddd2 }}</a>#} {# {% endfor %}#} {##} {# </li>#} {# {% endfor %}#} {#</ul>#} <!-- empty示例 --> {#<ul>#} {# {% for foo in d3 %}#} {# <li>{{ foo }}</li>#} {# {% empty %}#} {# <li>查询的内容啥也没有</li>#} {# {% endfor %}#} {##} {#</ul>#}
-
-
if 标签
{% if num > 100 %} <p>excellent</p> {% elif num == 100 %} <p>beautiful</p> {% else %} <p>nice</p> {% endif %} 1. Django的模板语言不支持连续判断,即不支持以下写法: {% if a > b > c %} ... {% endif %} 2. Django的模板语言中属性的优先级大于方法(了解) def xx(request): d = {"a": 1, "b": 2, "c": 3, "items": "100"} return render(request, "xx.html", {"data": d})
- if语句支持: and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。
-
with标签
-
使用一个简单地名字缓存一个复杂的变量,多用于给一个复杂的变量起别名,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的,注意等号不要加空格
# 两种方式 {% with total=business.employees.count %} {{ total }} <!--只能在with语句体内用--> {% endwith %} {% with business.employees.count as total %} {{ total }} {% endwith %}
-
-
csrf_token
安全认证机制 我们以post方式提交表单的时候,会报错,还记得我们在settings里面的中间件配置里面把一个csrf的防御机制给注销了啊,本身不应该注销的,而是应该学会怎么使用它,并且不让自己的操作被forbiden,通过这个东西就能搞定。 这个标签用于跨站请求伪造保护, 在页面的form表单里面(注意是在form表单里面)任何位置写上{% csrf_token %},这个东西模板渲染的时候替换成了<input type="hidden" name="csrfmiddlewaretoken" value="8J4z1wiUEXt0gJSN59dLMnktrXFW0hv7m4d40Mtl37D7vJZfrxLir9L3jSTDjtG8">,隐藏的,这个标签的值是个随机字符串,提交的时候,这个东西也被提交了,首先这个东西是我们后端渲染的时候给页面加上的,那么当你通过我给你的form表单提交数据的时候,你带着这个内容我就认识你,不带着,我就禁止你,因为后台我们django也存着这个东西,和你这个值相同的一个值,可以做对应验证是不是我给你的token,存储这个值的东西我们后面再学,你先知道一下就行了,就像一个我们后台给这个用户的一个通行证,如果你用户没有按照我给你的这个正常的页面来post提交表单数据,或者说你没有先去请求我这个登陆页面,而是直接模拟请求来提交数据,那么我就能知道,你这个请求是非法的,反爬虫或者恶意攻击我的网站。
-
模板继承
{% extends "base.html" %} # 写在开始 钩子:{% block title %} xxx {% endblock %} 钩子:{% block title %} xxx {% endblock title %} 钩子:{% block title %} {{ block.super }} # 显示继承模板的内容 xxx
-
组件
{% include 'navbar.html' %} # 引用组件的html文件 组件是提供某一完整功能的模块,如:编辑器组件,QQ空间提供的关注组件 等。 而插件更倾向封闭某一功能方法的函数。 这两者的区别在 Javascript 里区别很小,组件这个名词用得不多,一般统称插件。
8.5 自定义标签和过滤器
-
自定义过滤器
-
在settings中的 INSTALL_APPS 配置当前的app,不然Django无法找到自定义的过滤器
-
app应用文件夹中创建一个templatetags文件件,必须是这个名字
-
templatetags文件夹中创建一个 xx.py 文件,文件名字随便起
-
在新建的 xx.py 文件中创建自定义过滤器
from Django import template register = template.Library() # register固定名字,注册器 @register.filter def oo(v1): # 不带参数的过滤器,第一个参数v1是views视图函数中传过来的 s = v1 + 'xxoo' return s @register.filter def oo(v1,v2): # 带参数的过滤器 s = v1 + v2 return s
-
使用html文件中的数据
{% load xx %} {{ values|oo }} -- 无参数 {{ values|oo:'asdf' }} -- 有参数
-
参数最多两个
-
-
自定义标签
-
在settings中的 INSTALL_APPS 配置当前的app,不然Django无法找到自定义的过滤器
-
app应用文件夹中创建一个templatetags文件件,必须是这个名字
-
templatetags文件夹中创建一个 xx.py 文件,文件名字随便起
-
在新建的 xx.py 文件中创建自定义过滤器
from Django import template register = template.Library() # register固定名字,注册器 @register.simple_tag def mytag(v1,v2,v3): # 第一个参数v1是views视图函数中传过来的 s = v1 + v2 + v3 return s
-
使用
{% load xx %} {% mytag s1 '啦啦' '呵呵' %}
-
-
inclusion_tag : 多用于返回html代码片段
# views.py def index(request): lis = [11,22,33,44,55,66] return render(request,'index.html',{'lis':lis}) # index.html {% load one %} {% func lis %} # one.py from django import template register = template.Library() @register.inclusion_tag('test.html') # 将test.html里面的内容用下面函数的返回值渲染,然后作为一个组件一样,加载到使用这个函数的HTML里面 def func(v1): return {'data':v1} # v1 是传过来的lis列表 # test.html <ul> {% for d in data %} <li>{{ d }}</li> {% endfor %} </ul> 流程: 先执行index函数,将lis传给index.html,index.html中将lis作为func的参数传进去,v1=lis,在one.py中,将v1传给test.html进行渲染,最后将渲染的结果返回给index.html,在页面显示出来
8.6 静态文件
-
js,css,img 等都叫做静态文件,那么在关于jango中静态文件的配置,我们就需要在settings配置文件中写上下面内容
# 在项目目录下创建一个文件用来存放静态文件,通常名称为 : static STATIC_URL = '/static/' # 别名 STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static'), # 注意别忘了写逗号,第二个参数就是项目中你存放静态文件的文件夹名称 ]
-
目录 : 别名也是一种安全机制,浏览器上通过调试台你能看到的是别名的名字,这样别人就不知道你静态文件的名字了,不然别人就能通过这个文件夹路径进行攻击
-
前端页面引入静态文件的写法,因为别名也可能会修改,所以使用路径的时候通过load static来找别名,通过别名映射路径的方式来获取静态文件
-
{% static %}
{% load static %} <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
-
引用JS文件时使用
{% load static %} <script src="{% static "mytest.js" %}"></script>
-
某个文件多处被用到的可以存为一个变量
{% load static %} {% static "/images/hi.jpg" as myphoto %} <img src="{{ myphoto }}"></img>
-
注意:
-
一个html文件中写相对路径是需要注意一个问题
<form action="/login/"></form> <img src="/static/1.jpg" alt=""> 等标签需要写路径的地方,如果写的是相对路径,那么前置的/这个斜杠必须写上,不然这个请求会拼接当前网页的路径来发送请求,就不能匹配我们的后端路径了
-
9. 单表操作
1. ORM简介
-
ORM是 对象--关系--映射 的简称.实现了数据模型与数据库的解耦,即数据库的设计不需要依赖特定的数据库,通过简单的配置就可以轻松更换数据库
-
类对象 --> sql --> pymysql --> mysql服务端 --> 磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎
2. 建表操作
在python中orm的对应关系有三种:
类 ---------->表
类对象 ---------->行(记录)
类属性 ---------->表的字段(重点
-
创建表
# app 应用下的 models.py 文件中写 from django.db import models class UserInfo(models.Model): # UserInfo 为表名 id = models.AutoField(primary_key=True) name = models.CharField(max_length=10) age = models.IntegerField() current_date = models.DateField()
-
更多字段和参数
-
字段
<1> CharField 字符串字段, 用于较短的字符串. CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数. <2> IntegerField 用于保存一个整数. <3> DecimalField 一个浮点数. 必须 提供两个参数: <4> AutoField 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 自定义一个主键:my_id=models.AutoField(primary_key=True) 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.
-
参数
(1)null 如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False. (1)blank 如果为True,该字段允许不填。默认为False。 要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。 如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。 (2)default 字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值 (3)primary_key 如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True, Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为, 否则没必要设置任何一个字段的primary_key=True。 (4)unique 如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的 (5)choices 由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。 (6)db_index 如果db_index=True 则代表着为此字段设置数据库索引。 DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。 (7)auto_now_add 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。 (8)auto_now 配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
-
-
settings配置
-
若想将模型转为mysql数据库中的表,需要在 settings.py 中配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'库名', # 要连接的数据库,连接前需要创建好 'USER':'root', # 连接数据库的用户名 'PASSWORD':'', # 连接数据库的密码 'HOST':'127.0.0.1', # 连接主机,默认本级 'PORT':3306 , # 端口 默认3306 } }
-
项目文件夹下的 init.py文件中,写下面两句
import pymysql pymysql.install_as_MySQLdb()
-
执行数据库同步指令
python manage.py makemigrations # 生成记录,每次修改了models里面的内容或者添加了新的app,新的app里面写了models里面的内容,都要执行这两条 python manage.py migrate # 执行上面这个语句的记录来创建表,生成的表名字前面会自带应用的名字,例如:你的book表在mysql里面叫做app01_book表
-
3. 增加数据
-
model.py :
from django.db import models class Data(models.Model): name = models.CharField(max_length=10) age = models.IntegerField() current_data = models.DateField() def __str__(self): return self.name
-
创建方式一:
from app import models student_obj = models.Data( name = 'alex', age = 73, current_data= '2008-08-08' ) student_obj.save()
-
方式二(常用此创建)
from app import models new_obj = models.Data.objects.create( name = 'wusir', age = 83, current_data= '2008-08-15', ) print(new_obj) # Data object --> model对象 print(new_obj.name,new_obj.age) # .属性 可以获取对应字段的数据
-
方式三:
from app import models lis = [] for i in range(10): obj = models.Data( name = 'xin', age = 18, current_data= '2010-10-10' ) lis.append(obj) models.Data.objects.bulk_create(lis) # 批量插入,速度快
-
创建方式四: update_or_create 有就更新,没有就创建
from app import models models.Data.objects.update_or_create( name = 'taibai', # 有就更新 defaults={'age':89,'current_data':'2011-11-11'} name = 'xuefei', # 没有就创建 defaults={'age':18,'current_data':'2000-08-08'} )
4. 查询数据
1. 简单查询
# all() 查询所有的数据,返回的是queryset集合
all_objs = models.Data.objects.all()
print(all_objs)
for i in all_objs:
print(i.name) # 拿到每一个名字
# 条件查询: filter()方法, 返回的也是queryset集合,查询不到内容不会报错,返回一个<QuerySet []>空的queryset
objs = models.Data.objects.filter(id=15)
print(objs)
objs = models.Data.objects.filter(id=1,name='alex').update(name='eva',age=20)
print(objs)
打散的形式传参
objs = models.Data.objects.filter(**{'id':15,'name':'xuefei'})
print(objs)
# 条件查询 : get()方法,返回的是model对象,而且get方法有且必须只有一个结果
objs = models.Data.objects.get(id=15)
print(objs)
查询的数据不存在会报错,得到的结果有两个时会报错
# exclude(**kwargs):排除的意思,包含了与所给筛选条件不匹配的对象,没有不等于操作, objects控制器和queryset集合都可以调用,返回值是queryset类型.
ex = models.Data.objects.exclude(name='xin')
ex = models.Data.objects.all().exclude(name='xin')
print(ex)
# order_by(): queryset类型的数据来调用,查询结果进行排序,默认是按照id的升序排序的,返回值还是queryset类型,在字段前面加个 - 号,就是降序排序
ord = models.Data.objects.order_by('-age','id') # 多条件排序,按照age进行降序,age相同的按照id升序排序
print(ord)
# reverse() : queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型
rev = models.Data.objects.order_by('id').reverse()
print(rev)
# count() : queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。
con = models.Data.objects.all().count()
print(con)
# first() : queryset类型的数据来调用,返回第一条记录,得到的都是model对象
obj = models.Data.objects.all().first()
print(obj)
# last(): queryset类型的数据来调用,返回最后一条记录,结果为model对象类型
obj = models.Data.objects.all().last()
print(obj)
# exists() : queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False,空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits
# values(*field) : 用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列,model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。
# values_list(*field): 它与values()非常相似,它返回的是一个元组序列
obj = models.Data.objects.all().filter(age=16).values('name','age')
print(obj)
obj = models.Data.objects.all().filter(age=16).values_list('name','age')
print(obj)
# distinct(): values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录,结果还是queryset
query = models.Data.objects.all().values('age').distinct()
print(query)
2. 基于双下划线的模糊查询
mes = models.Data.objects.filter(price__in=[100,200,300]) # price值等于这三个里面的任意一个的对象
mes = models.Data.objects.filter(price__gt=100) # 大于,大于等于是price__gte=100,别写price>100,这种参数不支持
mes = models.Data.objects.filter(price__lt=100) # 小于
mes = models.Data.objects.filter(price__range=[100,200]) # sql的between and,大于等于100,小于等于200
mes = models.Data.objects.filter(title__contains="python") #title值中包含python的
mes = models.Data.objects.filter(title__icontains="python") #不区分大小写
mes = models.Data.objects.filter(title__startswith="py") #以什么开头,istartswith 不区分大小写
all_books = models.Book.objects.filter(pub_date__year=2012) #找2012年的所有书籍
all_books = models.Book.objects.filter(pub_date__year__gt=2012) # 找大于2012年的所有书籍
all_books = models.Book.objects.filter(pub_date__year=2019,pub_date__month=2) #找2019年月份的所有书籍
5. 删除数据
delete queryset 和model对象都可以调用
models.Student.objects.get(id=3).delete() # model对象来调用的delete方法
models.Student.objects.filter(name='Alex').delete()
models.Student.objects.all().delete() # 删除所有
6. 更改数据
model对象不能调用更新方法 报错信息'Student' object has no attribute 'update'
只能queryset调用
models.Student.objects.get(name='alex').update(age=38) # 报错
models.Student.objects.filter(name='alex').update(age=38)