本节内容
- HTTP协议
- wsgi协议和wsgiref模块
- Djano基本流程和配置
- Django的路由系统
- Django静态文件
- template模板语言
一 HTTP协议
1 HTTP简介
HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写。它的发展是万维网协会(World Wide Web Consortium)和Internet工作小组IETF(Internet Engineering Task Force)合作的结果,(他们)最终发布了一系列的RFC,RFC 1945定义了HTTP/1.0版本。
其中最著名的就是RFC 2616。RFC 2616定义了今天普遍使用的一个版本——HTTP 1.1。
HTTP协议具有如下特点:
(1) 基于TCP的80端口
(2) 基于请求-响应模式
(3) 无状态保存,不对请求和响应之间的通信状态进行保存
(4) 无连接,限制每次连接至处理一个请求,处理完成后即断开连接
2 HTTP中的方法
GET:从一个服务器获取一个资源
HEAD:只从服务器获取文档响应首部,而不获取响应内容
POST:向服务器发送要处理的数据
PUT:将请求的主体部分直接存储在服务器上
DELETE:请求删除服务器上指定的文档
TRACE:追踪请求到达服务器中间经过的代理服务器
OPTIONS:请求服务器返回对指定资源支持使用的请求方法
3 HTTP的报文格式
请求:request
响应:response
request报文
<method> <request-URL> <version>
<headers>
<entity-body>
resposene报文
<version> <status><reason-phrase>
<headers>
<entity-boday>
4 HTTP中的响应状态码
三位数字,如200,301,203,404,502,描述标记请求发生的状况
状态代码:
1XX:纯信息
2XX:成功类的信息
3xx:重定向类信息
4xx:客户端错误类的信息
5xx:服务器端错误类信息
常用状态码:
200:成功,请求的所有数据通过响应报文entity-body部分发送
301:请求的URL资源已被删除,但在响应报文中通过Location指明了资源现在的位置,Moved Permanently
302:与301相似,但在响应报文中通过Location指明资源现在所处临时新位置
304:客户端发出了条件式请求,但服务器的资源未曾发生改变,则通过响应此响应状态码通知客户端:Not Modified
401:需要输入账号和密码认证方能访问资源:Unauthorized
403:请求被禁止;Forbidden
404:服务器无法找到客户端请求的资源;Not Found
500:服务器内部错误;Internal Server Error
502:代理服务器从后端服务器收到了一条伪响应
5 HTTP请求头部和响应头部包含的内容
通用首部:
Date:报文的创建时间
Connetction:连接方式,如keep-alive,close
Via:显示报文经过的中间节点
Cache-Control:控制缓存
请求首部
Accpet:通知服务器自己可接受的媒体类型
Accept-Charset:通知服务器自己可接受的字符集
Accept-Encoding:通知服务器自己接受的编码格式,如GZIP
ACCPT-Language:接受的语言
Host:请求的服务器名称和端口号
Rerferer:包含当前正在请求资源的上一级资源
User-Agent:客户端代理
If-Modified-Since:自从指定的时间之后,请求的资源是否发生过修过
If-Unmodified-Since:自从指定的时间之后,请求的资源是否未过修过
If-None-Mathc:本地缓存中存储的文档的Etagb标签是否与服务器ETG不匹配
Authorization:向服务器端发送认证信息,如账号密码
Cookie:客户端向服务器端发送Cookie标识
Proxy-Authorization:向代理服务器认证
响应首部
Age:响应持续时长
Server:服务器程序的软件名称和版本
协商首部:某资源多种表示方法时使用
Accpet-Ranges:服务器可接受的请求范围类型
Vary:服务器查看其它首部列表
安全响应首部:
Set-Cookie:向客户端设置cookie
www.Authenticate:来自服务器对客户端的质询认证表单
6 Cookie和Session
Cookie和Session都为了用来保存状态信息,都是保存客户端状态机制,都是为了解决HTTP无状态的问题而产生
Session可以用Cookie来实现,也可以用URL回写的机制来实现。用Cookie来实现的Session可以认为是对Cookie更高级的应用。
Cookie和Session有以下明显的不同点:
(1) Cookie将状态保存在客户端,Session将状态保存在服务端
(2) Cookie是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器,cookie最早在RFC2109中实现,后续RFC2965做了增强。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies。Session并没有在HTTP的协议中定义;
(3) Session是针对每一个用户的,变量值保存在服务器上,用一个sessionID来区分是哪个用户session变量,这个值是通过用户的浏览器在访问的时候返回给浏览器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器。
(4) 就安全性来说:当你访问一个使用session 的站点,同时在自己机子上建立一个cookie,建议在服务器端的SESSION机制更安全些.因为它不会任意读取客户存储的信息。
关于session和Cookie在Django的具体使用请见:
7 通过sokcet 建立一个HTTP响应
def create_sk():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5)
return server
def handle_request(server):
print('server start....')
while 1:
conn,addr = server.accept()
data = conn.recv(1024)
print(data) # 接收到 http请求头部
conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
conn.send(b'<h1>hello world</h1>')
def run():
sk = create_sk()
handle_request(sk)
if __name__ == '__main__':
run()
二 wsgi协议和wsgiref模块
WSGI:Web Server Gateway Interface,是Python应用程序或框架和Web服务器之间的一种接口,而wsgiref模块就是python基于wsgi协议开发的服务模块。
自己定义一个web框架所需的几个主要文件
1 urls.py:路径与视图函数映射关系 --- url控制器
2 views.py: 视图函数,固定一个形式参数:environ
3 templates文件夹: html模板文件
4 models:在项目启动前创建表结构
通过wsgire模块自己搭建一个web服务器
main.py 主程序 :
from wsgiref.simple_server import make_server
import urls
def application(environ,start_response):
path = environ["PATH_INFO"] # 获取当前请求路径
print('server start')
start_response('200 OK', [('Content-Type', 'text/html')])
urls_list = urls.routers()
func = None
for i in urls_list:
if path == i[0]:
func = i[1]
if func:
return func()
else:
return [b'<h1>404 not found</h1>']
urls.py 控制器
from view import *
def routers():
urls_list = [
('/login', login),
]
return urls_list
view.py 视图函数
def login():
with open('templates/login.html', 'rb') as f:
data = f.read()
return [data]
http_server = make_server('', 8080 ,application)
http_server.serve_forever()
三 Djano基本流程和配置
1 MVC与MTV模型
MVC
Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,
模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求
MTV
Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:
M 代表模型(Model):负责业务对象和数据库的关系映射(ORM)。
T 代表模板 (Template):负责如何把页面展示给用户(html)。
V 代表视图(View):负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template
2 创建一个django project
danjgo-admin.py startproject mysite
manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等
settings.py ---- 包含乐项目默认设置, 包括数据库信息,调试标志及其他一些工作的表露
urls.py ---- 负责把URL模式映射到应用程序
3 Django的其他常用命令
python manage.py runserver ip:port (启动服务器,默认ip和端口为http://127.0.0.1:8000/)
python manage.py startapp appname (新建 app)
python manage.py syncdb (同步数据库命令,Django 1.7及以上版本需要用以下的命令)
python manage.py makemigrations (显示并记录所有数据的改动)
python manage.py migrate (将改动更新到数据库)
python manage.py createsuperuser (创建超级管理员)
python manage.py dbshell (数据库命令行)
python manage.py (查看命令列表)
四 Django的路由系统
每一个Django项目中都会有一个urls.py的文件,它的作用是根据用户请求的url调用不同视图函数来给用户返回不同页面
urlpatterns = [
path(url,view函数,参数,别名)
repath(正则表达式,view函数,参数,别名)
]
参数说明:
(1) 一个正则表达式字符串,或者一个url
(2) 一个可以调用的对象,通常称为一个视图函数,
(3) 可选的要传递给视图函数的默认参数(字典形式)
(4) 一个可选的name参数
1 示例:
from django.contrib import admin from django.urls import path,re_path import views urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), re_path(r'^article/2001$', views.article_year), # 匹配正则匹配到的路径 re_path(r'^article/([0-9]{4})', views.article_year), re_path(r'^article/([0-9]{4})/([0-9]{2})/$', views.article_month), re_path(r'^article/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})',views.article_month), # 正则命名,视图函数的形参要与正则命名的一致 ]
注意事项:
(1) 要捕获URL中的值,用括号括起来,会当作参数传入视图函数
(2) 没有必要添加一个斜线,因为每个URL都有。例如:^articles不是^/articles。
(3) 在'r'前面的每个正则表达式字符串中是可选的,但建议。它告诉Python字符串是“原始” -没有什么字符串中应该进行转义。
2 多个APP进行路由分发(Including)
如果一个Django项目里面有多个APP那么大家共用一个url路由很容易造成混淆,于是使用include分发让每个APP拥有自己单独的url
from django.conf.urls import re_path,include
from django.contrib import admin
from app1 import views
urlpatterns = [
re_path(r'^app1/',include('app1.urls')),
re_path(r'^app2/',include('app1.urls')), #注意include的是字符串形式的 文件路径;
re_path(r'^',views.error),
]
3 使用url别名的示例
url中还支持name参数的配置,如果配置了name属性,在模板的文件中就可以使用name值来代替相应的url值.
urlpatterns = [
re_path(r'^index',views.index,name='bieming'),
re_path(r'^admin/', admin.site.urls),
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/([0-9]{4})/$', views.year_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
###################
def index(req):
if req.method=='POST':
username=req.POST.get('username')
password=req.POST.get('password')
if username=='alex' and password=='123':
return HttpResponse("登陆成功")
return render(req,'index.html')
#####################
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# <form action="/index/" method="post">#}
{# 这里只要使用bieming即可代替/index #}
<form action="{% url 'bieming' %}" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="submit">
</form>
</body>
</html>
4 名称空间
名称空间是表示标识符的可见范围,一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的,
在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他命名 空间。
由于name没有作用域,Django在反解析URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,立即返回
我们在开发项目时,会经常使用name属性反解除URL,当不小心在不同的app的urls中定义相同的name时,可能会导致URL反解错误,为了避免这种情况引入了命名空间
project的urls.py:
urlpatterns = [
re_path(r'^app1/',include(('app1.urls',"app01")),
re_path(r'^app2/',include(('app2.urls',"app02")),
]
app01.urls
urlpatterns = [
re_path(r'^index/',index,name="index"),
]
app02.urls
urlpatterns = [
re_path(r'^index/',index,name="index"),
]
app01.views
from django.core.urlresolvers import reverse
def index(request)
return HttpResponse(reverse("app01:index"))
app02.views
from django.core.urlresolvers import reverse
def index(request)
return HttpResponse(reverse("app02:index")
5 django2.0中 path的用法
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/, views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/',views.moutn_archive),
path('articles/<int:year>/<int:month>/<slug>/, views.article_detail),
]
基本规则:
(1) 使用尖括号(<>)从url中捕获值。
(2) 捕获值中可以包含一个转化器类型(converter type),比如使用<int:name>捕获一个整数变量,如果没有转化器,将匹配任何字符串,也包括'/'字符
(3) 无需添加前倒斜杠
Django默认支持的转化器
(1) str,匹配除了路径分隔符(/)之外的飞车空字符串,这是默认的形式
(2) int,匹配正整数,包含0
(3) slug,匹配字母,数字及横杠,下划线组成的字符串
(4) uuid,匹配格式化uuid,如0745194d3-6885-417e-a8a8-6c931e272f00
(5) path,匹配任何非空字符串,包含了路径分隔符
自定义转换器
对于一些复杂的使用,可以自定义装换器。转换器是一个类或接口,它的要求有三点:
regex类属性,字符串类型
to_python(self,value)方法,value是由类属性regex所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中
to_url(self,value)方法,和to_python相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用
示例:
class FourDigYearConverter: regex = '[0-9]{4}' def to_python(self,value): return int(value) def to_url(self,value): return '%04d' %value 使用register_converter将其注册到URL配置中 from django.urls import register_converter,path from . import converters, views # 自己写的类模块导入尽量 register_converter(converters.FourDigYearConverter, 'yyyy') urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<yyyy:year>', views.path_month) ]
五 Django静态文件
1 static静态文件
我们在项目中一般情况下html模板都需要引入js css等静态文件来渲染网页,下面会介绍Django引入这些静态文件的方式:
1 首先先要在项目中创建一个名叫static的文件夹
2 在Django的settings中添加一行配置, 拼接static文件夹的路径
STATICFILES_DIRS = [
os.path.join(BASE_DIR,"static")
]
3 在html直接引入static路径下的内容
<script src="/static/jquery-3.3.1.js"></script>
2 media静态文件
Media配置
在项目的应用下生成一个media文件夹
在settings下配置:MEDIA_ROOT = os.path.join(BASE_DIR,"media")
如果用户一旦配置了MEDIA_ROOT:
avatar_obj=request.FIELS.get("avatar")
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar_obj)
Django会将文件对象下载到MEDIA_ROOT中的avatar文件夹中(如果没有avatar,Django会自动创建), user_obj的avatar存的是文件对象
Meida 配置值MEDIA_URL:
让浏览器直接访问到media中的数据
在settings.py里配置MEDIA_URL="/meida"
# media配置
from django.views.static import serve
from cnblog import settings
re_path(r"media/(?P<path>.*)$", serve, {"document_root":settings.MEDIA_ROOT})
六 template模板语言
1 模板之过滤器
语法
{{ obj|filter_name:param }}
default
如果一个变量是false或者为空,使用给定的默认值,否则使用变量的值.
{{ value|default:"nothing" }}
length
返回值的长度,它对字符串和列表都起作用。例如
{{ value|length }}
如果value是['a','b','b','d']那么输出4
filesizeformat
将格式化为一个"人类可读的"文件尺寸(例如13kb,4.1mb,1024bytes等待).例如:
{{ value|filesizeformat }}
如果value是123456789,输出将会是117.7MB
date
如果 value = datetime.datetime.now()
{{ value|date:"Y-m-d" }}
slice
如果 value = "hello world"
{{ value|slice:"2-1" }}
truncatechars
如果字符串字符多于指定的字符数量,那么会被截断。截断字符串可以翻译的省略号序列("...")结尾
参数:要截断的字符数
{{ value|truncatechars:9 }}
safe
Django的模板中会对HTML标签和JS等语法进行自动转义,但是有的时候我们不希望这些HTML元素被转义,
在Django中关系HTML自动转义有两种方式,如果是一个单独的变量我们可以通过过滤"|safe"的方式告诉Django这段代码是安全的不必转义
value = "<a href="">点击</a>
{{ value|safe }}
2 模板之标签
标签看起来是这样的:{% tag %}。标签比变量更加复杂:一些在输出中创建文件,一些通过循环或逻辑来控制流程,
一些加载其后的变量将使用到的额外信息添加到模板中。
for标签 遍历每一个元素 {% for person in person_list %} <p>{{ pserson.name }} <p> { % endfor %} 可以利用{% for obj in list reversed %}反向完成循环。 遍历一个字典: {% for key,val in dic.items %} <p>{{ key }}:{{ val }}<p> {% endfor % } for ... empty for标签带有一个可选的{% empt %}从句,以便在给出的组是空的或者没有找到时,可以有所操作 {% for person in pserson_list %} <p>{{ person.name }}</p> {% enpty %} <p>sorry,no person here</p> {% endfor %} if 标签 {% if %}会对一个变量求值,如果他的值是True(存在,不为空且不是boolean类型的false值),对应的内容块会输出 {% if num >100 or num <0 %} <p>无效</p> {% elif num > 80 and num < 100 %} <p>优秀</p> {% else %} <p>凑合吧</p> {% endif %} with 使用一个简单的名字缓存一个复杂的变量。 例如: {% with total=business.employees.count %} {{ total }} employee{{ total|pluralize }} {% endwith %}
3 自定义标签
(1) 在应用目录下创建一个templatetags目录(必须)
(2) 创建任意.py文件, 如:add100.py
from django import template from django.utils.safestring import mark_safe register = template.Library() # register是固定变量名,不能改变 @register.simple_tag def my_add100(value1) return value1 + 100 @register.simple_tag def my_input(id,arg): result = "<input type="text" id='%s' class='%s'/>" %id(id,arg,) return make_server(result) @register.filter # 自定义过滤器 def multi_filter(x,y): return x*y
#注意:自定义标签和自定义过滤器的区别在于标签可以传多个参数而过滤器最多只能传2个但是过滤器可以使用if语句
(3) 在使用自定义simple_tag的html文件导入之前创建add100.py文件夹
{% load add100 %}
(4) 使用simple_tag
{% my_add100 5%}
{% my_input 'id_username' 'hide'%}
(5) 在setting中配置当前app, 不然django无法找到自定义的simple_tag
INSTALLED_APPS = "app01".
4 模板之继承
(1) 首先先将一些公共的版块写成一个基础模板, 如果模一块内容需要扩展,{% block contnet %} {% endblock %},相当于留了一些空间等待子模板重写里面的内容
(2) 在子模板的第一行引入模板{% extends 'base.html' %}
(3) 在子模块引入{% block contnet %} {% endblock %}然后重写里面的内容