标签:工程师 decode ctc template box now() 独立 键值 设置ip
前言
前段时间业务比较繁忙,没时间更新博客,从这周开始将继续为各位更新博客。本次分享的主题是Django的基础部分,涵盖Django MTV三部分,并通过一个简单的班级管理系统来说明如何使用Django进行开发,好啦,开始今天的主题吧!
一.浅谈MVC、MTV和MVVM
要学习Django,我们很有必要了解下MVC,MTV和MVVM三种模式。
【001】MVC
MVC(Model View Controller 模型-视图-控制器)是一种Web架构的模式(本文不讨论桌面应用的MVC),它把业务逻辑、模型数据、用户界面分离开来,让开发者将数据与表现解耦,前端工程师可以只改页面效果部分而不用接触后端代码,DBA可以重新命名数据表并且只需更改一个地方,无需从一大堆文件中进行查找和替换。MVC模式甚至还可以提高代码复用能力。现在几乎所有的Web开发框架都建立在MVC模式之上。 当然,最近几年也出现了一些诸如MVP, MVVM之类的新的设计模式。 但从技术的成熟程度和使用的广泛程度来讲,MVC仍是主流。
MVC模式的三要素
MVC的特点是通信单向的:
【002】MTV
和Rails、Spring、Laravel等其他语言的Web框架不一样,在Python的世界中,基本(除了Pylons)都使用了MVC的变种MTV(Model Templates View 模型-模板-视图):
这里有一点大家要注意区分MVC中的View和MTV中的View:MVC中的View的目的是「呈现哪一个数据」,即它只是负责渲染数据,然后呈现给用户效果;而MTV的View的目的是「数据如何呈现」,说白了,它负责从Model中取出数据,然后调用相关的模板,从而完成对整个流程的控制。
也就是把MVC中的View分成了视图(展现哪些数据)和模板(如何展现)2个部分,而Contorller这个要素由框架自己来实现了,我们需要做的就是把(带正则表达式的)URL对应到视图就可以了,通过这样的URL配置,系统将一个请求发送到一个合适的视图。需要注意,Flask这种微框架就不是一个MVC模式的,因为它没有提供Model,除非集成了SQLAlchemy之类的ORM进来。
二.Web框架本质
对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。当我们在浏览器中输入一个网址时,会发生如下的情况:
基于上面的需求,我们就可以自己实现一个Web框架,在这里我们只需要实现Socket服务端即可。来看下面一段简单到不行的Socket网络编程代码:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((‘127.0.0.1‘, 8000))
sock.listen(5)
while True:
conn, addr = sock.accept()
data = conn.recv(8096)
conn.send(b"OK")
conn.close()
可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码就是它们的祖宗。用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定?你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。这个规则就是HTTP协议,以后你发送请求信息也好,回复响应信息也罢,都要按照这个规则来。这个规则就是HTTP协议,如果你对HTTP协议还不熟悉,可以参考笔者前面的这篇文章。
我们所要做的是让我们的Web服务能够根据用户请求的URL不同而返回不同的内容,同时给浏览器返回HTML源代码,代码如下:
import socket import pymysql sk = socket.socket() sk.bind((‘127.0.01‘, 9090)) sk.listen(5) def get_html_content(file_name): with open(file_name, encoding=‘utf-8‘) as file_obj: content = file_obj.read() return content def get_record(): conn = pymysql.connect(host="127.0.0.1", port=3306, user=‘root‘, password=‘cisco‘, db="day48", charset=‘utf8‘) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id, name, balance from user") user_list = cursor.fetchall() cursor.close() conn.close() return user_list def index(): index_data = get_html_content("index.html") user_list = get_record() ret = "" for item in user_list: ret += """ <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> </tr> """.format(item[‘id‘], item[‘name‘], item[‘balance‘]) data_new = index_data.replace(‘@@xx@@‘, ret) return data_new def login(): login_data = get_html_content("login.html") return login_data url_func_map = [ ("/index", index), ("/login", login) ] while True: conn, addr = sk.accept() data = conn.recv(8096) header_str = data.decode(‘utf-8‘).split(‘\r\n‘)[0] request_target = header_str.split(‘ ‘)[1] func_name = None for i in url_func_map: if i[0] == request_target: func_name = i[1] break if func_name: response = func_name() else: response = "404,页面不见了" conn.send(b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n") conn.send(response.encode(‘utf-8‘)) conn.close()
上述代码模拟了一个web服务的本质.而对于真实开发中的Python Web程序来说,一般分为两部分:服务器程序和应用程序。服务器程序负责对Socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么它们就可以配合使用。一旦标准确定,双方根据约定的标准各自进行开发即可。WSGI(Web Server Gateway Interface)就是这样一种规范,它定义了使用Python编写的Web APP与Web Server之间接口格式,实现Web APP与Web Server间的解耦。
三. Django基础
【001】常用命令如下:
#安装Django,默认会安装最新版本的Django,目前生产环境下最稳定的Django版本是1.8.2,
#因此我们一般指定安装稳定的版本:pip install django==1.8.2
pip3 install django
# 在终端下创建Django项目,名为DjangoProject
$ django-admin startproject mysite
# Django项目环境终端,例如启动Django项目,创建项目中的应用,我们都使用该命令:
$ python manage.py shell
"""
注意如果在终端创建应用程序,我们需要确保此时的路径应该和 manage.py 是同一目录
"""
$ python manage.py startapp student
# 启动django
$ python manage.py runserver # 如果不指定IP地址和端口,默认启动在本机IP 127.0.0.1的8000端口上
$ python manage.py runserver 8080 # 指定启动Django项目的端口8080
# 如果设置IP地址为0.0.0.0,表明局域网中的所有IP地址都可以通过8000端口访问Django项目
$ python manage.py runserver 0.0.0.0:8000
"""
根据当前项目中的模型类来生成数据库脚本,并将脚本映射到数据库中去;
"""
$ python manage.py makemigrations
# 运行应用模型变化到数据库
$ python manage.py migrate
# admin创建管理员用户,按照提示输入用户名和对应的密码就好了邮箱可以留空,用户名和密码必填
$ python manage.py createsuperuser
# 修改用户密码
$ python manage.py changepassword username
【002】Django基本目录结构及作用
mysite/ # 项目的容器,名称自定义 manage.py # 命令行实用工具,以各种方式与该Django项目进行交互 mysite/ # 实际的Python项目 __init__.py # 空文件,导入不出错 settings.py # 这个Django项目配置 urls.py # 这个Django项目的URL声明; 一个Django驱动网站的“目录” wsgi.py # 一个入口点为WSGI兼容的Web服务器,以满足您的项目
【003】静态文件配置
概述: 静态文件交由Web服务器处理,Django本身不处理静态文件。简单的处理逻辑如下(以nginx为例): URI请求-----> 按照Web服务器里面的配置规则先处理,以nginx为例,主要求配置在nginx.conf里的location |---------->如果是静态文件,则由nginx直接处理 |---------->如果不是则交由Django处理,Django根据urls.py里面的规则进行匹配 以上是部署到Web服务器后的处理方式,为了便于开发,Django提供了在开发环境的对静态文件的处理机制。
static配置:
初学者往往会搞不明白STATIC——URL和STATICFILES_DIRS 的区别,如下所示:
STATIC_URL = ‘/static/‘ # 这是引用名 STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), # 静态文件存放的位置,例如项目中涉及到css,js,image等文件,这里推荐使用
# 列表的形式,要不然可能会报错:ImproperlyConfigured: Your STATICFILES_DIRS setting is not a tuple or list # 官方文档对此有详细的描述:https://docs.djangoproject.com/en/dev/howto/static-files/;
] TEMPLATE_DIRS = (os.path.join(BASE_DIR, ‘templates‘),)
注意点如下:
必须严格按照static目录下的路径来,也就是说在HTML中引用静态文件,开头必须是/static/目录
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
<link href="/static/lib/sweetalert/sweetalert.css" rel="stylesheet">
如下图所示:
Media型文件在settings.py中的配置:
# in settings:
MEDIA_URL="/media/"
MEDIA_ROOT=os.path.join(BASE_DIR,"app01","media","upload")
# in urls:
from django.views.static import serve
url(r‘^media/(?P<path>.*)$‘, serve, {‘document_root‘: settings.MEDIA_ROOT}),
‘‘‘ 静态文件的处理又包括STATIC和MEDIA两类,这往往容易混淆,在Django里面是这样定义的: MEDIA:指用户上传的文件,比如在Model里面的FileFIeld,ImageField上传的文件。如果你定义 MEDIA_ROOT=c:\temp\media,那么File=models.FileField(upload_to="abc/")#,上传的文件就会被保存到c:\temp\media\abc eg: class blog(models.Model): Title=models.charField(max_length=64) Photo=models.ImageField(upload_to="photo") 上传的图片就上传到c:\temp\media\photo,而在模板中要显示该文件,则在这样写 在settings里面设置的MEDIA_ROOT必须是本地路径的绝对路径,一般是这样写: BASE_DIR= os.path.abspath(os.path.dirname(__file__)) MEDIA_ROOT=os.path.join(BASE_DIR,‘media/‘).replace(‘\\‘,‘/‘) MEDIA_URL是指从浏览器访问时的地址前缀,举个例子: MEDIA_ROOT=c:\temp\media\photo MEDIA_URL="/data/" 在开发阶段,media的处理由django处理: 访问http://localhost/data/abc/a.png就是访问c:\temp\media\photo\abc\a.png 在模板里面这样写<img src="/media/abc/a.png"> 在部署阶段最大的不同在于你必须让web服务器来处理media文件,因此你必须在web服务器中配置, 以便能让web服务器能访问media文件 以nginx为例,可以在nginx.conf里面这样: location ~/media/{ root/temp/ break; } 具体可以参考如何在nginx部署django的资料。 ‘‘‘
【004】视图层之路由配置系统(views)
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
url(正则表达式, views视图函数,参数,别名),
] 参数说明: 一个正则表达式字符串 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串 可选的要传递给视图函数的默认参数(字典形式) 一个可选的name参数
来看下面的例子:
urlpatterns = [
url(r‘login.html/‘, views.login, name="login")]
r‘login.html/‘表示匹配用户在浏览器中请求访问的URL地址,通常配合正则表达式进行精确匹配;
views.login表示一旦匹配到前面指定的地址,就执行这里的视图函数,login是views.py文件中的视图函数,一般会有
多个函数存在于views.py文件中,通常情况下,一般使用在指定应用的目录的urls.py文件下,导入,例如我新建了一个
应用app01,在它下面的urls.py文件中,我们定义具体url和函数的对应关系:
# 先导入具体的包,然后再指定具体url和视图函数的对应关系
from app01 import views
url(r‘login.html/‘, views.login, name="login")
name属性取别名。
来看如下的具体需求:
1.通常在form表单中,我们往往使用post方法向服务器传递数据,同时要指定哪个url地址来处理数据,我们往往
采用硬编码的形式将路径写死:
<form action="/login/" method="post">
{% csrf_token %}
<p>姓名:<input type="text" name="name"> </p>
<p>密码:<input type="password" name="pwd"> </p>
<p><input type="submit" name="提交"> </p>
</form>
上面的需求表示我们将用户输入的数据交给/login/处理,
现在有20个这样的需求,都是交给/login/处理,但是突然由于业务变动,我们想将
用户输入的数据交给应用app01下面的login.html,即处理地址变为:app01/login.html,
按照我们硬编码的方式,那就要重新修改上述20个函数,是不是太麻烦了!!此时就可以用到
name属性了,我们可以将form表单中的action属性设置为jinja2的模板形式:
action="{% url "login" %}",采用url指定地址即可,url后面是别名,这样,在后台urls.py中,我们
可以设置name属性,其值为这里的别名:login,例如:
url(r‘login.html/‘, views.login, name="login")
这样设置后,凡是用户端能够匹配到r‘login.html/‘这个正则表达式的url,我都采用login函数处理,并且将
login.html模板文件中的action地址替换为:app01/login.html,我们来看实际的运行效果:
以上我们称之为url的反向解析。下面通过实际例子分析name参数和include参数的作用
分析:用户在浏览器中输入:http://127.0.0.1:8090/app01/login.html/;首先到
与Django同名的app目录的urls.py文件中匹配:
url(r‘app01/‘, include(‘app01.urls‘)匹配到第二条,这里我们来插播一个新的关键字参数include。
还记得我们开始创建Django项目时,会把所有与业务相关的url和视图函数的匹配关系全部都放到urls.py文件中,如下
所示:
urlpatterns = [
url(r‘^teacher_list/‘, teacher_list),
url(r‘^add_teacher/‘, add_teacher),
url(r‘^delete_teacher/‘, delete_teacher),
url(r‘^modal_teacher_list/‘, modal_teacher_list),
url(r‘^modal_edit_teacher/‘, modal_edit_teacher),
url(r‘^class_list/‘, class_list),
url(r‘^add_class/‘, add_class),
url(r‘^delete_class/‘, delete_class),
url(r‘^edit_class/‘, edit_class),
]
这只是我们学习时候的案例,在现实生产环境中,往往对应上百条的url,肯定不能全都写到url.py文件中,理由
有2点,如果一个url和视图函数的对应关系写错了,那么其他应用都运行不了,这就造成了严重的耦合,另外为了
保证应用之间的独立性,让它们互不影响,我们也不应该让它们都放在一起。于是出现了include关键字,也即路由分发策略
拿我们刚才的例子来说:
url(r‘app01/‘, include(‘app01.urls‘),用户输入:http://127.0.0.1:8090/app01/login.html/
首先匹配到app01,然后执行后面的视图函数,当遇到include关键字,此时相当于告诉Django你去include
里面的参数地址中寻找视图函数,此时Django会去应用app01中的urls.py文件中去寻找视图函数,内容如下:
urlpatterns = [
url(r‘login.html/‘, views.login, name="login"),
url(r‘index.html/‘, views.index)
]
到达app01.urls.py文件中,继续拿用户剩下的地址login.html/进行匹配,因此会首先匹配到第一条正则表达式,于是
执行后面的视图函数login,这里由于我们设置了url反向解析,因此模板文件中使用url进行反向解析的实际地址会被替换为
前面正则表达式匹配到的路径:login.html。从运行效果可以看到,action中的路径是绝对路径,即,action的实际地址是
/app01/login.html/。到此为止,我们总算讲完了这几个关键字参数。
【005】URL具体配置规则
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),
]
上面是一些具体的匹配例子,我们来看如下几个需要注意的点:
NOTE:
1 URL一旦匹配成功则不再继续往下面匹配;
2 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号;例如url(r‘^articles/([0-9]{4})/$‘, views.year_archive),
我们在[0-9]{4}加入了(),表示我们想从这个url中捕获任意一个4位整数
3 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
4 每个正则表达式前面的‘r‘是可选的但是建议加上。
一些请求的例子:
/articles/2005/3/ 不匹配任何URL模式,因为列表中的第三个模式要求月份应该是两个数字。
/articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。
/articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数
views.month_archive(request, ‘2005‘, ‘03‘)。关于调用函数的传参问题,我们将在下面一节中详细描述。
【006】2.1.2 有名分组(named group)
下面我们来看有名分组的几个例子:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r‘^articles/2003/$‘, views.special_case_2003),
url(r‘^articles/(?P<year>[0-9]{4})/$‘, views.year_archive),
url(r‘^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$‘, views.month_archive),
url(r‘^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$‘, views.article_detail),
]
这个实现与我们前面的例子几乎没有太大区别,但细心的同学可能已经发现了,在捕获具体值得时候,我们加上了
?p<year>,例如url(r‘^articles/(?P<year>[0-9]{4})/$‘, views.year_archive),我们在[0-9]{4} 加上了(?p<year>)
这表示我们为捕获到的值取一个名字year,并将该值以关键字传参的形式传递给后面的函数views.year_archive。
例如让地址 /articles/2005/03/ 去匹配上述的url地址,匹配成功后,我们将调用views.month_archive(request, year=‘2005‘, month=‘03‘)函数;
再例如:articles/2003/03/03/ ,请求将调用函数views.article_detail(request, year=‘2003‘, month=‘03‘, day=‘03‘) 。
通过对比上面的示例可知,使用(?p<关键字名称>)捕获的值将作为关键字参数而不是位置参数传递给视图函数。
【007】URLConf在什么上查找
URLconf 在请求的URL 上查找,将它当做一个普通的Python 字符串。不包括GET和POST参数以及域名。
例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/。
在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/。
URLconf 不检查请求的方法。换句话讲,所有的请求方法 —— 同一个URL的POST、GET、HEAD等等 —— 都将路由到相同的函数。
【008】每个捕获的参数都作为一个普通的Python 字符串传递给视图,无论正则表达式使用的是什么匹配方式。例如,下面这行URLconf 中:
url(r‘^articles/(?P<year>[0-9]{4})/$‘, views.year_archive) views.year_archive() 的year 参数将是一个字符串
【009】指定视图参数的默认值
# URLconf 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), ] # View (in blog/views.py) def page(request, num="1"): 在上面的例子中,两个URL模式指向同一个视图views.page —— 但是第一个模式不会从URL 中捕获任何值。如果第一个模式匹配, page() 函数将使用num参数的默认值"1"。如果第二个模式匹配,page() 将使用正则表达式捕获的num 值。
三. 视图层
视图层主要负责处理用户的请求并返回响应。返回的内容可以是HTML内容的网页,或重定向,或404错误,或一个XML文件,或一个形象,字符串等等。
在Django中,http请求会产生两个核心对象:http请求:HttpRequest对象 http响应:HttpResponse对象 所在位置:django.http,下面我们分别来梳理下HttpRequest和HttpResponse的一些重点属性和方法:
#从django.http模块中导入HttpResponse类[1] from django.http import HttpResponse import datetime def current_datetime(request): #[2] now=datetime.datetime.now() html="<html><body>现在时刻:%s.</body></html>" %now return HttpResponse(html) # 注意:这是一段很简单、简陋的例子 # 在这个(views.py)视图中每一个函数称作视图函数,视图函数都以一个HttpRequest对象为第一个参数,该参数通常命名为request,注意这里的参数也可以命名为其他的名称,只是request
这个名称能够做到见名知意。
由上面示例得到视图函数第一个参数是一个HttpRequest对象,那么通过这个对象可以拿到一些信息,如下:
A:HttpRequest
【01】HttpRequest对象中的常见属性:
1.HttpRequest.body
一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML,Json等。
但是,如果要处理表单数据的时候,推荐还是使用 HttpRequest.POST 。
另外,我们还可以用 python 的类文件方法去操作它,详情参考 HttpRequest.read() 。
2.HttpRequest.path
一个字符串,表示请求的路径组件(不含域名),例如我们在浏览器中输入访问地址:127.0.0.1:8000/music/bands/the_beatles/
HttpRequest.path得到的路径就是:"/music/bands/the_beatles/"
3.HttpRequest.method
一个字符串,表示请求使用的HTTP 方法。必须使用大写。
例如:"GET"、"POST"
4.HttpRequest.encoding
一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 ‘utf-8‘)。
这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。
接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。
如果你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。
5.HttpRequest.GET
一个类似于字典的对象,包含 HTTP GET 的所有参数。详情请参考 QueryDict 对象。
例如我们在浏览器中输入访问地址:127.0.0.1:8000/student_list/?edit_id=3 表示我们想编辑的学生ID号为3
在视图函数中,可以使用request.GET.get(‘edit_id‘)获取ID值3,从而方便我们去数据库中修改。
6.HttpRequest.POST
一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成 QueryDict 对象。
POST 请求可以带有空的 POST 字典 —— 如果通过 HTTP POST 方法发送一个表单,但是表单中没有任何的数据,QueryDict 对象依然会被创建。
因此,不应该使用 if request.POST 来检查使用的是否是POST 方法;应该使用 if request.method == "POST"
我们通常使用该属性来获取用户在输入框中的用户名和密码等信息,例如:
username = request.POST.get(‘username‘)
password = request.POST.get(‘password‘)
另外:如果使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。
7.HttpRequest.COOKIES
一个标准的Python 字典,包含所有的cookie。键和值都为字符串,例如获取服务端发送的cookies值:
request.COOKIES.get("login1") 只要存在该cookie,那么证明用户处于登入状态。
8.HttpRequest.FILES
一个类似于字典的对象,包含所有的上传文件信息。
FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。
注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会
包含数据。否则,FILES 将为一个空的类似于字典的对象。
9.HttpRequest.session
一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。
完整的细节参见会话的文档。例如我们一般使用request.session.get(‘key‘)获取对应的value值
注意:键值对的值是多个的时候,比如checkbox类型的input标签,select标签,例如一个用户可能有多个爱好,那么当我们使用
select标签返回数据给后台时是一个列表,此时后台应该使用request.POST.getlist("hobby")
【02】HttpRequest对象的常用方法
1.HttpRequest.get_full_path()
返回 path,如果可以将加上查询字符串。
例如:"/music/bands/the_beatles/?print=true"
2.HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt=‘‘, max_age=None)
返回签名过的Cookie 对应的值,如果签名不再合法则返回django.core.signing.BadSignature。
如果提供 default 参数,将不会引发异常并返回 default 的值。
可选参数salt 可以用来对安全密钥强力攻击提供额外的保护。max_age 参数用于检查Cookie 对应的时间戳以确保Cookie 的时间不会超过max_age 秒。
复制代码
>>> request.get_signed_cookie(‘name‘)
‘Tony‘
>>> request.get_signed_cookie(‘name‘, salt=‘name-salt‘)
‘Tony‘ # 假设在设置cookie的时候使用的是相同的salt
>>> request.get_signed_cookie(‘non-existing-cookie‘)
...
KeyError: ‘non-existing-cookie‘ # 没有相应的键时触发异常
>>> request.get_signed_cookie(‘non-existing-cookie‘, False)
False
>>> request.get_signed_cookie(‘cookie-that-was-tampered-with‘)
...
BadSignature: ...
>>> request.get_signed_cookie(‘name‘, max_age=60)
...
SignatureExpired: Signature age 1677.3839159 > 60 seconds
>>> request.get_signed_cookie(‘name‘, False, max_age=60)
False
复制代码
3.HttpRequest.is_secure()
如果请求时是安全的,则返回True;即请求通是过 HTTPS 发起的。
4.HttpRequest.is_ajax()
如果请求是通过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是否是字符串‘XMLHttpRequest‘。
大部分现代的 JavaScript 库都会发送这个头部。如果你编写自己的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 可以工作。
如果一个响应需要根据请求是否是通过AJAX 发起的,并且你正在使用某种形式的缓存例如Django 的 cache middleware,
你应该使用 vary_on_headers(‘HTTP_X_REQUESTED_WITH‘) 装饰你的视图以让响应能够正确地缓存。
B:HttpResponse对象
对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象,这就是为什么我们在
视图函数中都会看到return关键字的原因,相当于这是给客户端浏览器的响应,因为前面我们说过HTTP协议是基于请求响应模式的;我们来看看在HttpResponse对象上扩展的常用方法:
【01】render(request, template_name[, context])
结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
例如:return render(request, "class_list.html", {‘class_list‘: class_list})
这句话的意思是我使用context对象{‘class_list‘: class_list}去渲染模板文件:class_list.html,最终将渲染完成的html文件
返回给客户端浏览器,然后客户端浏览器再为终端用户解释HTML源码,于是我们看到了绚丽的HTML页面,就是这么简单。
参数:
request: 用于生成响应的请求对象,第一个参数必须是request,表明这是客户端的请求对象
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。
status:响应的状态码。默认为200。
我们所说的视图渲染,其实就是使用context对象中的数据去模板文件template_name中替换需要替换的数据,而在Django中,我们需要
遵循一套替换规则,所以产生了Jinja2模板语言。context对象中的数据就是我们与后台数据库交互获取的数据,这就构成了整个动态网站的基础。
有时我们仅仅是需要将页面呈现给终端用户,并不需要使用数据去渲染模板文件,所以此时可以不需要context对象,如下:
return render(request, "class_list.html"),关于render与Redirect的区别,本节稍后会给出详细解释。
【02】redirect 函数
参数可以是:
一个模型:将调用模型的get_absolute_url() 函数
一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称
一个绝对的或相对的URL,将原封不动的作为重定向的位置。
默认返回一个临时的重定向;传递permanent=True 可以返回一个永久的重定向。
此方法可称之为页面跳转,HTTP状态码为302,
表示达达到某种条件时,如再登陆淘宝界面,登陆成功之后会跳转到用户的主界面
默认情况下,为临时重定向;通过 permanent=True 设置永久重定向
下面我们来看看render()方法和redirect方法的区别:
【01】render(request, template_name[, context])
结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
例如:return render(request, "class_list.html", {‘class_list‘: class_list})
这句话的意思是我使用context对象{‘class_list‘: class_list}去渲染模板文件:class_list.html,最终将渲染完成的html文件
返回给客户端浏览器,然后客户端浏览器再为终端用户解释HTML源码,于是我们看到了绚丽的HTML页面,就是这么简单。
参数:
request: 用于生成响应的请求对象,第一个参数必须是request,表明这是客户端的请求对象
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。
status:响应的状态码。默认为200。
我们所说的视图渲染,其实就是使用context对象中的数据去模板文件template_name中替换需要替换的数据,而在Django中,我们需要
遵循一套替换规则,所以产生了Jinja2模板语言。context对象中的数据就是我们与后台数据库交互获取的数据,这就构成了整个动态网站的基础。
有时我们仅仅是需要将页面呈现给终端用户,并不需要使用数据去渲染模板文件,所以此时可以不需要context对象,如下:
return render(request, "class_list.html"),关于render与Redirect的区别,本节稍后会给出详细解释。
【02】redirect 函数
参数可以是:
一个模型:将调用模型的get_absolute_url() 函数
一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称
一个绝对的或相对的URL,将原封不动的作为重定向的位置。
默认返回一个临时的重定向;传递permanent=True 可以返回一个永久的重定向。
此方法可称之为页面跳转,HTTP状态码为302,
表示达达到某种条件时,如再登陆淘宝界面,登陆成功之后会跳转到用户的主界面
默认情况下,为临时重定向;通过 permanent=True 设置永久重定向
【03】render方法和redirect的区别(重点掌握)
从使用方法的角度上:
render方法表示如果页面需要模板语言渲染,需要将数据库的数据加载到html,那么此时就需要使用render方法;
而redirect方法表示仅仅是重定向到某个网页,并没有数据渲染模板文件的过程;
从客户端与服务端的交互角度上:
使用render()方法时候,客户端浏览器和服务器只需要进行一次HTTP请求-响应链接,而render()方法需要进行二次请求-响应
链接,我们以登入的案例为例来进行分析:
def login(request):
if request.method == ‘POST‘:
username = request.POST.get("name")
password = request.POST.get("pwd")
if username == ‘carson‘ and password == ‘cisco123‘:
return redirect(‘/index.html/‘)
else:
return render(request, "login.html")
return render(request, "login.html")
第一次:用户发送Get请求,获取登入页面的url:http://127.0.0.1:8000/login.html/;由于是get请求,所以并没有数据,
接着去urls.py中匹配到login视图函数,然后调用执行login函数——login(request);返回结果return render(request, "login.html")
所以当我们在浏览器中输入登入页面的url:http://127.0.0.1:8000/login.html/;会看到登入界面,由于这个过程非常快速,所以
我们压根感觉不到;
第二次,用户接收到登入页面,开始输入用户名和密码;数据通过POST方法发送到后台,后台的处理地址是action中指定的地址:/login/
所以我们依然去urls.py中匹配,同样匹配到login视图函数,此时执行POST请求方法,用户名和密码正确,于是乎,我们就可以访问首页
注意这里使用的是redirect()跳转到index.html,这里隐藏了如下两点含义:
1.当用户输入的用户名和密码正确,服务端给出的响应是:return redirect(‘/index.html/‘),客户端在接受到该条命令后,发现
这是一条让我跳转的命令,因为此时的HTTP状态码是302,所以客户端浏览器会再次向服务端发送一个请求:
http://127.0.0.1:8000/index.html/,此时这里是一个get请求,还是走上面的流程,最终匹配到index视图函数,然后将index.html模板返回给
客户端浏览器,最后客户端浏览器在终端为我们渲染出效果,于是乎,我们的体验就是:输入了正确的用户名和密码后,就跳转到
首页,输入错误后,直接停留在login.html
综上所述:render()方法只是建立了一次完整的HTTP请求-响应链接;而redirect()方法建立了2次完整的HTTP请求-响应链接。
---恢复内容结束---
标签:工程师 decode ctc template box now() 独立 键值 设置ip
原文地址:http://www.cnblogs.com/pyspark/p/7994392.html