Django视图

Django 2018-06-24

一、视图概述

Django用视图这个概念封装处理用户请求并返回响应的逻辑。
定义视图函数相关的URL(网址) (即规定 访问什么网址对应什么内容)。或者说视图接受Web请求,并响应Web请求。

视图的本质就是一个python函数.

1.概念

- 响应
    网页(包含重定向、错误视图404、错误视图500、错误视图400)
    JSON数据
- 过程
    用户在浏览器中输入网址www.zyz.com/meituan/detail.html
    django获取到网址信息,截取掉IP和端口 meituan/detail.html
    对应虚拟路径和文件名,在url管理器中逐个匹配urlconf列表 detail
    调用视图管理器中的detail,返回对应结果给浏览器

2.定义视图

from  django.http import HttpResponse
# 没有使用任何的模板
def index(request):
    return HttpResponse('hello zyz!!!')

3.配置url (简单)

# 修改project/urls.py文件
from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('myapp.urls', namespace='app')),   # 会定位到myapp.urls中
]


# 在对应应用中appname中,创建urls.py文件(与上面文件类似)
from django.conf.urls import url
from . import views     # 导入视图

urlpatterns = [
    url(r'^$', views.index, name='index')    # 匹配到的就是views中的首页
]

二、URL配置

1.配置流程

- URL配置文件
    settings.py文件中ROOT_URLCONF=project.urls (默认已经配置好)
- project.urls.py文件
    urlpatterns是一个url实例列表
    url对象(正则表达式,视图名称,名称)
    例如: url(r'^admin/', admin.site.urls,name='admin')
- url匹配正则注意事项
    想要从url中获取一个值,需要对正则加小括号(即分组)
    匹配正则前方不需要加反斜杠
    正则前面需要加r表示字符串不转义

2.引入其他配置

- 在应用中创建urls.py文件,配置该应用内的路由
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^(\d+)/$', views.detail, name='detail'),
]

- 在工程目录中urls.py分发到对应应用中
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app/', include('myapp.urls', namespace='app')),
]

3.配置url (带参数)

# 描述(在views.py中定义的视图)
def detail(request,num):
    return HttpResponse('detail-%s' % num)


# 在appname/urls.py中配置即可        
urlpatterns = [
    url(r'^$', views.index), 
    # 添加括号,其实就是正则表达式中的组的概念。
    # 而匹配到的就是可以给到 detail(request,num) 函数中的num
    url(r'^(\d+)/$', views.detail)
]

4.路径参数

请求地址中的参数及是路径参数,在url中正则表达式中有几个分组,即表示有几个参数。
在视图函数中,除第一个参数是request,其他参数和url是一一对应关系。

# urls.py中
urlpatterns = [
    url(r'^detail/(\d+)/(\d+)/$', views.detail, name='detail'),
]

# views中,注意要一一对应
def detail(request, a, b):
    return HttpResponse('商品详情')

# 模板中,反向解析书写
<a href="{% url 'app:detail' 3 4 %}">商品1</a>

在上述写法中,特别容易出错,所以就可以用下面的写法操作,在url中添加参数名称:

# urls.py中
urlpatterns = [
    url(r'^detail/(?P<type_name>\w+)/(?P<goods_id>\d+)/$', views.detail, name='detail'),
]

# views中,注意要一一对应
def detail(request, type_name, goods_id):
    return HttpResponse('商品详情')

# 模板中,反向解析书写
<a href="{% url 'app:goods_detail' '家电' 2 %}">冰箱</a>
或
<a href="{% url 'app:goods_detail' type_name='家电' goods_id=2 %}">冰箱</a>

. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字 等价于[^A-Za-z0-9_]
\s 匹配任意的空白符
\d 匹配数字
^ 匹配字符串的开始
$ 匹配字符串的结束

5.反向解析

随着功能的增加会出现更多的视图,可能之前配置的正则表达式不够准确,于是就要修改正则表达式,但是正则表达式一旦修改了,之前所有对应的超链接都要修改,真是一件麻烦的事情,而且可能还会漏掉一些超链接忘记修改,有办法让链接根据正则表达式动态生成吗? 就是用反向解析的办法。

- 概述: 如果在视图、模板中使用了硬编码链接,在url配置发生改变时,动态生成链接地址
- 解决: 在使用链接时,通过url配置的名称,动态生成url地址
- 作用: 使用url模板或试图中

模板中: {% url 'namespace:name' value1 value2 ...%}
视图中: redirect('namespace:name', arg1, arg2...)

三、视图函数

1.定义视图

- 本质: 视图即是一个函数
- 参数: 一个HttpRequest对象,通过正则表达式获取的参数
- 位置: 一般会在views.py中定义

2.错误视图

- 404视图   
    找不到网页时返回(url匹配不成功)
    在temmplates目录下定义404.html({{request_path}}会输出导致错误的网址)
    配置settings.py(DEBUG如果为True永远不会调用404.html页面)
    配置settings.py(ALLOWED_HOSTS=['*'])

- 505视图
    视图代码出现错误(服务器错误)

- 400视图
    错误出现在客户的操作

四、HttpRequest对象

1.概述

- 服务器接收http请求后,会根据报文创建HttpRequest对象
- 视图的第一个参数就是HttpRequest对象
- django创建的,之后调用视图时传递给视图

2.属性

- path 
    请求的完整路径(不包括域名和端口)
- method 
    请求的方式,常用有GET、POST
- encoding
    浏览器提交数据的编码方式
- GET [GET请求参数]
    类似字典对象,包含get请求的所有参数
- POST [POST请求参数]
    类似字典对象,包含post请求的所有参数
- FILES
    类似字典对象,包含所有上传的文件
- COOKIES 
    字典,包含所有的cookie
- session
    类似字典对象,表示当前会话

3.方法

- request.is_ajax()
    通过XMLHttpRequest发起的,就会返回True 【判断是否为ajax操作】

4.QueryDict对象

- request对象中的GET、POST都属于QueryDict对象
- get() 
    根据键获取值(只能获取一个)
    也可以通过方括号的方式获取数据(如果不存在会报错)
- getlist()
    根据键获取值(返回的是一个列表,可以获取多个值)

5.GET属性

- 获取浏览器传递给服务器的数据
- 例如: http://127.0.0.1:8000/zyz/get1?a=1&b=2&c=3
def get1(request):  
    a = request.GET.get('a')
    b = request.GET['b']
    c = request.GET.get('c')

    return HttpResponse('a:%s  b:%s  c:%s' % (a,b,c))
- 例如: http://127.0.0.1:8000/zyz/get2?a=1&a=2&b=3
def get2(request): 
    a = request.GET.getlist('a')
    a1 = a[0]
    a2 = a[1]

    b = request.GET['b']

    return HttpResponse('a1:%s  a2:%s  b:%s' % (a1, a2, b))

GET请求,请求的数据会添加到URL之后,以?分割URL和传输数据,多个参数用&连接;

6.POST属性

def register(request):
    # 例如在输入框中对应name属性
    name = request.POST['user']
    passwd = request.POST.get('passwd')
    tel = request.POST.get('tel')

    return HttpResponse('注册成功,%s正在登录中....' % (name))

使用表单提交post请求时,需要关闭csrf(settings.py中的MIDDLEWARE内的'django.middleware.csrf.CsrfViewMiddleware', )
POST请求: 请求的数据放置在是HTTP的请求体中;

7.什么是 HTTP

超文本传输协议(HTTP)的设计目的是保证客户机与服务器之间的通信。
HTTP 的工作方式是客户机与服务器之间的请求-应答协议。
客户端(浏览器)向服务器提交 HTTP 请求;服务器向客户端返回响应。

8.HTTP请求方法

1    GET        请求指定的页面信息,并返回实体主体。
2    HEAD    类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
3    POST    向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
4    PUT        从客户端向服务器传送的数据取代指定的文档的内容。
5    DELETE    请求服务器删除指定的页面。
6    CONNECT    HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
7    OPTIONS    允许客户端查看服务器的性能。
8    TRACE    回显服务器收到的请求,主要用于测试或诊断。

9.GET和POST请求

- get一般是从服务器上获取数据,post一般是向服务器传送数据;
- get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制;
- get安全性非常低,post安全性较高();
  如果没有加密,他们安全级别都是一样的,随便一个监听器都可以把所有的数据监听到(即也没有所谓的安全性高低问题);

五、HttpResponse对象

1.概述

- 用于给浏览器返回数据
- HttpRequest对象是django创建的,HttpResponse对象是自己创建的
    response = HttpResponse()

2.返回操作

- 不调用模板(HttpResponse)
    def index(request):
        return HttpResponse('hello zyz!!!')
        
- 调用模板(render)
    原型: render(request,templateName,[context...])
    作用: 结合数据和模板,返回完整的HTML页面
    参数: request(请求体对象)、templateName(模板路径)、context(传递给模板的数据)
    例如: return render(request,'myapp/students.html',{'students':studentList})

3.属性

- content 
    返回的内容
- charset
    返回的编码类型
- status
    返回的状态码(200/304/404)
- content-type
    指定输出的MIME类型

4.方法

- init 
    使用页面内容实例化HttpResponse对象
- write(content)
    以文件的形式写入
- flush()
    以文件的形式输出缓冲区
- set_cookie(key,value)
    设置cookie
- delete_cookie(key)
    删除cookie
    删除一个不存在的key,就当什么都没发生

5.子类HttpResponseRedirect

- 作用
    重定向,服务器端的跳转
- 简写
    return  HttpResponseRedirect('/redirect2') 

    from django.shortcuts import redirect
    return redirect('/redirect2')

反向解析不带参数: return redirect('meituan:detail')
反向解析带参数: return redirect('meituan:detail', 10, 5)

6.子类JsonResponse

返回json数据,一般用于异步请求
def jsonrequest(request):
    stu = {
        'name':'zhangsan',
        'score':90,
        'age':20
    }
    return JsonResponse(stu)

Content-type类型: application/json

六、会话技术之cookie

1.概述

- http协议是无状态的,每次请求都是一次新的请求,不会保持之前的请求状态
- 客户端与服务器的一次通信就是一次回话
- 实现状态保持,在客户端或服务器存储有关回话数据
- 存储方式
    cookie: 所有数据存储在客户端(不要存储敏感的数据)
    session: 所有数据存储在服务端(在客户端用cookie存储session_id)
- 状态保持的目的
    在一段时间内跟踪请求者的状态,可以实现跨页面访问当前的请求者数据

会话技术,其实也就是为了状态的保持,延长请求的生命周期。

2.cookie概述

cookie 客户端的会话技术(数据存储在客户端);
cookie 本身由浏览器生成,通过Response将cookie写到浏览器上,下次访问,浏览器会根据不同的规则携带cookie过来;

3.cookie使用

- 创建响应
  response = HttpResponse()

- 设置cookie
    response.set_cookie(key,value)

- 获取cookie
    request.COOKIE.get(key, defaultvalue=None)
    
- 删除cookie
    response.delete_cookie(key)
    
- 支持过期时间
    默认: 关闭浏览器即cookie自动清除(Django)
    response.set_cookie(key,value,max_age=None,expires=None)
        max_age: 整数,指定cookie过期时间(单位秒)
        expires: 整数,指定过期时间,还支持datetime或timedelta指定具体时间
        max_age和expires两个选一个指定


        max_age=0           浏览器关闭失效
        max_age=Node        永不过期
        max_age=Int数值     指定过期时间(单位秒)
        expires             过期时间,和max_age基本一致,多一个时间差
        例如: expires=timedelta(days=10)    10天后过期
        例如: expires=timedelta(minutes=1)  1分钟

cookie不能跨浏览器!

4.cookie使用场景

用户系统状态、购物车(京东的)...

Cookie交互流程

七、会话技术之session

1.session概述

session 服务端的会话技术;
session 依赖于cookie;
    将session在数据库中的session_key当做sessionid存储在cookie中;
在Django中数据被存储在数据库中,并做了数据安全,使用base64进行编码;

2.启用session

在settings文件中(默认是已经启用)
    INSTALLED_APPS: 'django.contrib.sessions',
    MIDDLEWARE: 'django.contrib.sessions.middleware.SessionMiddleware',

3.使用session

启用session后,每个HttpRequest对象中都会有session属性(类似字典的对象)
- 设置session
    request.session[key] = value
    
- 获取session
    request.session.get(key, defaultvalue=None)

- 清除当前会话
    response.delete_cookie('sessionid')
    del request.session[key]
    
- 同时清除cookie 和 session
    request.session.flush()

要注意浏览器中会有缓存问题!

4.设置过期时间

set_expiry(value)
    value是整数(秒数): request.session.set_expiry(10)
    value是时间对象
    value是0: 关闭浏览器时失效
    value是None: 永不过期

默认有效时间为两周

5.存储session的位置

- 数据库
    默认存储在数据库中,在settings.py中添加
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'
- 缓存
    只存储在本地内容中,如果丢失不能找回,但速度会比在数据库中快
    SESSION_ENGINE = django.contrib.sessions.backends.cache'
- 数据库和缓存
    有限从本地缓存中读取,读取不到再去数据库中获取
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'

6.使用redis缓存session

- 安装redis: pip3 install django-redis-sessions
- 在settings.py中添加
    # redis服务相关信息
    SESSION_ENGINE = 'redis_sessions.session'
    SESSION_REDIS_HOST = 'localhost'
    SESSION_REDIS_PORT = 6379
    SESSION_REDIS_DB = 0  # 选择数据库
    SESSION_REDIS_PASSWORD = '123456'  # 如果没有密码,则不需要
    SESSION_REDIS_PREFIX = 'session'  # 前缀
- 使用时还需要启动redis
    redis-server

session交互流程

八、会话技术之token

1.token概述

token 服务端会话技术;
token 实际就是手动实现session;

2.token生成

# token唯一标识
# 策略1: 时间戳 + 随机数 + 公司域名 + ip信息
# 策略2: 时间戳 + 域名
# ...


def generate_token():
    token = str(time.time()) + str(random.random())
    # md5算法
    md5 = hashlib.md5()
    md5.update(token.encode('utf-8'))
    
    token = md5.hexdigest()
    
    return token

MD5消息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。【摘要算法、哈希算法】

3.UUID

通用唯一标识符(Universally Unique Identifier), 对于所有的UUID它可以保证在空间和时间上的唯一性。
通过MAC地址, 时间戳, 命名空间, 随机数, 伪随机数来保证生成ID的唯一性。 UUID可以被用作多种用途, 既可以用来短时间内标记一个对象, 也可以可靠的辨别网络中的持久性对象。

- uuid1() 基于时间戳 【不建议使用】
    由MAC地址、当前时间戳、随机数生成;
    但MAC的使用同时带来安全性问题,局域网中可以使用IP来代替MAC;
    例如: token = uuid.uuid1()
    
- uuid2() 基于分布式计算环境DCE 【Python中没有这个函数】
    算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID;
    实际中很少用到该方法;
    
- uuid3(namespace,name) 基于名字的MD5散列值   【推荐使用】
    通过计算名字和命名空间的MD5散列值得到;
    保证了同一命名空间中不同名字的唯一性和不同命名空间的唯一性;
    但同一命名空间的同一名字生成相同的uuid;
    例如: token = uuid.uuid3(uuid.NAMESPACE_DNS, 'test')
    
- uuid4() 基于随机数            【不推荐使用】
    由伪随机数得到,有一定的重复概率,该概率可以计算出来;
    例如: token = uuid.uuid4()
    
- uuid5(namespace,name) 基于名字的SHA-1散列值 【推荐使用】
    算法与uuid3相同,不同的是使用 Secure Hash Algorithm 1 算法;
    例如: token = uuid.uuid5(uuid.NAMESPACE_DNS, 'test')

namespace 可以是任意 uuid 字符串,例如: temp = uuid.uuid5(uuid.uuid4(), 'test')

九、数据安全

服务器的数据对任何人来说都应该是不可见的(不透明的);
可以使用常见的摘要算法对数据进行摘要(md5,sha);
如果使用了数据安全,那么就需要在所有数据验证的地方都加上 数据安全;

# 例如使用MD5
def password_set(password):
    md5 = hashlib.md5()
    md5.update(password.encode("utf-8"))
    return md5.hexdigest()

十、验证码

1.作用

- 在用户注册、登录页面的使用使用,为了防止暴力请求,减轻服务器的压力
- 防止CSRF的一种方式

request.session.get["k1"],如果不存在则会报错,为了防止出错可以request.session.get('k1',none)

2.示例(视图之生成验证码)

# 验证码
def verifycode(request):
    # 导入绘图模块
    from PIL import Image,ImageDraw,ImageFont
    # 导入随机函数模块
    import random
    # 文件操作
    import io

    # 定义变量,用于图片的背景色、宽、高
    # random.randrange()返回指定递增基数集合中的一个随机数
    bgcolor = (random.randrange(20,100),random.randrange(20,100),random.randrange(20,100))
    width = 100
    heigh = 50

    # 创建图片
    image = Image.new('RGB',(width,heigh),bgcolor)

    # 创建画笔对象
    draw = ImageDraw.Draw(image)

    # 调用画笔的point函数,绘制噪点
    for i in range(0,100):
        xy = (random.randrange(0,width),random.randrange(0,heigh))
        fill = (random.randrange(0,255),255,random.randrange(0,255))
        draw.point(xy,fill=fill)

    # 定义验证码的备选值
    str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'

    # 随机选取4个值作为验证码
    rand_str = ''
    for i in range(0,4):
        rand_str += str[random.randrange(0,len(str))]

    # 构建字体对象
    # font = ImageFont.truetype(path) # 指定路径加载字体
    # font = ImageFont.load_default() # 加载一个默认的字体
    # truetype(font=None, size=10, index=0, encoding="", layout_engine=None)
    font = ImageFont.truetype('fonts/Songti.ttc',40)
    # font = ImageFont.truetype('fonts/STXINGKA.ttf',40)

    #构建字体颜色
    fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))

    # 绘制4个字
    # def text(self, xy, text, fill=None, font=None, anchor=None, *args, **kwargs):
    draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
    draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
    draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
    draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)

    # 释放画笔
    del draw

    # 存入session,用于验证
    request.session['verify'] = rand_str

    # 文件操作
    buff = io.BytesIO()
    # 将图片保存在内存中,文件类型png
    image.save(buff,'png')

    # 将内存中图片数据返回给客户端,MIME类型为图片类型
    # 直接是返回验证码图片
    return HttpResponse(buff.getvalue(),'image/png')

PIL(Python Imaging Library)是Python一个强大方便的图像处理库,名气也比较大,不过只支持到Python 2.7。Pillow是PIL的一个派生分支,但如今已经发展成为比PIL本身更具活力的图像处理库。目前最新版本是3.0.0。
ython 3.x 安装Pillow: pip install Pillow

3.示例(视图之登录)

# 登录操作
def verifycodefile(request):
    # 获取到session的flag标志值
    flag = request.session.get("flag", True)
    str = ''
    if flag == False:
        str = "请重新输入"

    # 清空session
    request.session.clear()

    return render(request, 'meituan/verifycodefile.html', {"flag": str})

4.示例(视图之登录验证码验证)

# 登录验证
def verifycodecheck(request):
    # 输入的验证码
    # upper()转为大写
    code1 = request.POST.get('verifycode').upper()
    # session验证码
    code2 = request.session['verify'].upper()
    
    if code1 == code2:  # 验证成功
        return render(request,'meituan/success.html')
    else:   # 重新登录操作
        request.session["flag"] = False
        return redirect('/meituan/verifycodefile/')

5.示例(url管理器)

urlpatterns=[
    url(r'^verifycode/$',views.verifycode), # 验证码测试
    url(r'^verifycodecheck/$',views.verifycodecheck),    # 验证操作
    url(r'^verifycodefile/$',views.verifycodefile),  # 带验证码登录
]
  • 示例(模板)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录 | 验证码测试</title>
</head>
<body>
    <!--验证码-->
    <!--默认什么都添加,就是直接显示-->


    <!--添加登录功能验证-->
    <form action="/meituan/verifycodecheck/" method="post">
        {%csrf_token%}
        <input type="text" name="verifycode" />
        <img src="/meituan/verifycode/" /> <br>
        <input type="submit" value="验证" /> <br>
        <span>{{flag}}</span>
    </form>

</body>
</html>

本文由 zyz 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

楼主残忍的关闭了评论