3.Django视图高级

内置错误视图

1、视图

一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应。代码写在哪里也无所谓,只要它在你的应用目录下面。但是为了方便视图一般被定义在“应用/views.py”文件中。

视图的第一个参数必须为HttpRequest实例,还可能包含下参数如:

  • 通过正则表达式组获得参数

视图必须返回一个HttpResponse对象或子对象作为响应。

2、错误视图

Django内置处理HTTP错误的视图,主要错误及视图包括

  • 404错误:page not found视图
  • 500错误:server error视图
  • 400错误:bad request视图
  • 403错误:permission_denied视图

如果想要看到错误视图,而不是错误调试信息的需要设置setting.py配置文件的调试开关设置为False:ALLOWED_HOSTS允许所有主机访问。

DEBUG = False
ALLOWED_HOSTS = ["*",]

404错误及视图

  • 将请求地址进行url匹配后,没有找到匹配的正则表达式,则调用404视图,这个视图会调用404.html的模板进行渲染
  • 404视图默认情况下定义在'django.views.defaults.page_not_found'
  • django 自带404模板
  • 视图传递变量request_path给模板,是导致错误的URL

自定义错误模板: 在项目的templates目录下创建一个名字叫404.html的模板文件: 当发生404错误时,404视图会调用templates中的模板文件而不再调用自带的模板。

其他错误处理方式相同。

3、自定义错误视图

Django中默认的错误视图对于大多数web应用已经足够了,但是如果你需要任何自定义行为,只要在你的URLconf中指定下面的处理器(在其他任何地方设置它们不会有效)。

在应用下的urls.py中自定义错误视图。 handler404覆盖了page_not_found()视图:

handler404 = 'personal_blog.views.my_page_not_found_view'

404自定义错误视图

def my_page_not_found_view(request):
    '''404错误视图'''
    return HttpResponse('请求不存在')

访问错误url

其他错误视图跟404错误一样。 handler500覆盖了server_error()视图:

handler500 = 'mysite.views.my_custom_error_view'

handler403覆盖了permission_denied()视图:

handler403 = 'mysite.views.my_custom_permission_denied_view'

handler400覆盖了bad_request()视图:

handler400 = 'mysite.views.my_custom_bad_request_view'

4、模型类get方法错误处理

在讲模型的get方法的时候讲到过,使用get方法如果数据不存在会抛出一个DoesNotExist的异常. 比如用户访问博客的详细页请求传过来一个id据库中不存在,那么用户就会看到报错页面。

get_object_or_404的介绍: 我们原来调用django 的get方法,如果查询的对象不存在的话,会抛出一个DoesNotExist的异常。 现在我们调用django get_object_or_404方法,它会默认的调用django 的get方法, 如果查询的对象不存在的话,会抛出一个Http404的异常,返回404错误页面。这样用户就不会觉得是服务器异常。而是自己访问的路径不对。

from django.shortcuts import get_object_or_404 

def detail(request, id):
    # post = Post.post_object.get(pk=id)

    post = get_object_or_404(Post,pk=id)  # 调用get_object_or_404方法,如果没有错误返回结果

    return render(request, 'personal_blog/detail.html', context={"post": post})

get_object_or_404 第一个参数是需要查询的模型类, 第二个参数是 查询条件。

HttpRequest 对象

1、HttpRequest

服务器接收到http协议的请求后,Django会建立一个包含请求元数据的 HttpRequest 对象,这个对象不需要我们创建,直接使用服务器构造好的对象使用就可以

2、属性

scheme: 一个字符串,表示请求的方案(通常是http 或https)。

body: 一个字节字符串,表示原始HTTP 请求的正文。

path: 一个字符串,表示请求的页面的完整路径,不包含域名

method:一个字符串,表示请求使用的HTTP 方法。常用值包括:'GET'、'POST'

encoding:一个字符串,表示提交的数据的编码方式(如果为None 则表示使用DEFAULT_CHARSET 设置,一般为utf-8)。这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。接下来对属性的任何访问(例如从GET 或 POST 中读取数据)将使用新的encoding 值

GET: 一个类似于字典的对象,包含HTTP GET 的所有参数

POST: 一个包含所有给定的HTTP POST参数的类字典对象,提供了包含表单数据的请求。

COOKIES:一个标准的Python 字典,包含所有的cookie。键和值都为字符串。

FILES: 一个类似于字典的对象,包含所有的上传文件。FILES 中的每个键为<input type="file" name="" />中的name。

session:一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。

META:一个标准的Python 字典,包含所有的HTTP 头部。

  • CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
  • CONTENT_TYPE —— 请求的正文的MIME 类型。
  • HTTP_ACCEPT —— 响应可接收的Content-Type。
  • HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
  • HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
  • HTTP_HOST —— 客服端发送的HTTP Host 头部。
  • HTTP_REFERER —— Referring 页面。
  • HTTP_USER_AGENT —— 客户端的user-agent 字符串。
  • QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
  • REMOTE_ADDR —— 客户端的IP 地址。
  • REMOTE_HOST —— 客户端的主机名。
  • REMOTE_USER —— 服务器认证后的用户。
  • REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
  • SERVER_NAME —— 服务器的主机名。
  • SERVER_PORT —— 服务器的端口(是一个字符串)。

在浏览器中开发者工具->网络 中可以看到请求信息:

示例:

  • scheme,path,encoding:
def info(request):
    # HttpRequest对象属性
    str = '请求的方案:%s</br>路径:%s</br>编码:%s'%(request.scheme,request.path,request.encoding)

    return HttpResponse(str)

  • method 表示请求使用的HTTP方式

创建三个视图:

def method1(request):
    return render(request,'personal_blog/method1.html')
def method2(request):
    return HttpResponse(request.method)  # 返回请求方式
def method3(request):
    return HttpResponse(request.method)   # 返回请求方式

配置URL:

    url(r'^method1/$', views.method1),
    url(r'^method2/$', views.method2),
    url(r'^method3/$', views.method3),

创建模板:method1.html 写入下面内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="/blog/method2/">方式一:get</a>
</br>
</br>
</br>
<form method="post" action="/blog/method3/">
    <input type="submit" value="方式二:post">
</form>
</body>
</html>

浏览其中访问:http://127.0.0.1:8000/blog/method1/

点击链接 方式一:get,转到method2,浏览器接收到请求方式'GET'

点击链接 方式二:post, 转到method3,会出现拒绝访问。django默认开启 csrf 跨站请求伪造,后面会详细讲,这里先将csrf关闭。

将项目目录的settings.py文件,将MIDDLEWARE_CLASSES项的csrf注释

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    #'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
)

注释之后重新访问:

META 一个标准的Python 字典,包含所有的HTTP 头部。

def metainfo(request):
    m = request.META
    str=''

    # 将http请求头拼接返回给浏览器,查看http请求头信息
    for key,value in m.items():
       str += '%s :  %s'%(key,value) + '</br>'
    return HttpResponse(str)

配置url

  url(r'^metainfo/$', views.metainfo),

在浏览器访问; http://127.0.0.1:8000/blog/metainfo/

3、QueryDict 对象

在HttpRequest 对象中,GET 和POST 属性是django.http.QueryDict 的实例,它是一个自定义的类似字典的类,用来处理同一个键带有多个值。 与python字典不同,QueryDict类型的对象用来处理同一个键带有多个值的情况

方法: get:根据键获取值,如果一个键同时拥有多个值将获取最后一个值,如果键不存在则返回None值,可以设置默认值进行后续处理

getlist:根据键获取值,值以列表返回,可以获取指定键的所有值如果键不存在则返回空列表[],可以设置默认值进行后续处理

这两个方法用于获取 url ? 号后面的参数

get():

dict = request.GET
dict.get('键''默认值')   # 如果GET参数里没有这个键,那么将使用默认值。默认值是一个可选参数

getlist():

dict = request.GET
dict.getlist('键''默认值')   # 获取一键多值的情况,返回指定键的所有值的列表

4、GET属性

请求参数: 在请求地址结尾使用?,之后以“键=值”的格式拼接,多个键值对之间以&连接

例如:

https://www.baidu.com/s?ie=UTF-8&wd=python 

ie=UTF-8&wd=python  ?之后的就是请求参数

请求参数 键 ie,wd 值分别为 UTF-8,python

在Django中可以使用HttpRequest对象的GET属性获得get方式请求的参数,如果是post请求则用POST属性获取请求参数。

键是开发人员在编写代码时指定的,值可能是用户输入,或者动态生成。

示例:

在创建一个模板get.html,编辑两个a标签

personal_blog/get.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<a href="/blog/get1/?a=1&b=2"> get:一键一值</a>
<br>
<br>
<br>
<a href="/blog/get2/?a=1&b=2&a=3"> get2:一键多值</a>

</body>
</html>

写三个视图,一个视图返回get.html模板,另外两个用来接收get1,get2的请求返回请求参数。

def get(request):
    return render(request,'personal_blog/get.html')


def get1(request):

    dict = request.GET #获取get请求的参数,返回一个QueryDict对像
    a = dict.get('a')  # get方法获取值
    b = dict.get('b')
    c = dict.get('c','我是默认值c')  #不存在键c,使用默认值
    return HttpResponse('a:%s ,b:%s ,c:%s'%(a,b,c))


def get2(request):
    dict = request.GET  # 获取get请求的参数,返回一个QueryDict对像
    a = dict.getlist('a')   # getlist方法获取一键多值的参数,返回列表
    b = dict.get('b')

    return HttpResponse('a:%s ,b:%s' % (a, b))

配置url

    url(r'^get/$', views.get),
    url(r'^get1/$', views.get1),
    url(r'^get2/$', views.get2),

访问: http://127.0.0.1:8000/blog/get/

点击get:一键一值, 视图接收到请求参数 a,b c不存在则使用默认值。

点击get2:一键多值 视图接收到请求参数 a为多值,getlist返回包含所有值得列表。

5、POST属性

在进行form表单提交的时候将 method 方式改成post方式,提交表单时会发起POST请求。需要使用HttpRequest对象的POST属性接收参数,POST属性返回QueryDict类型的对象

在表单进行提交时,控件name属性的值作为键,value属性的值为值,构成键值对提交,如果控件没有name属性那么将不进行提交。

radio 控件,name属性的值相同为一组单选,将选中的提交 checkbox控件,name属性的值相同为一组,被选中的项都会被提交,出现一键多值的情况

创建一个form表单的模板 personal_blog/post.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>表单</title>
</head>
<body>
<form method="post" action="/blog/post1/">
    姓名:<input type="text" name="uname"/><br>
    密码:<input type="password" name="upwd"/><br>
    性别:<input type="radio" name="ugender" value="1"/><input type="radio" name="ugender" value="0"/><br>
    爱好:<input type="checkbox" name="uhobby" value="抽烟"/>抽烟
    <input type="checkbox" name="uhobby" value="喝酒"/>喝酒
    <input type="checkbox" name="uhobby" value="烫头"/>烫头<br>
    <input type="submit" value="提交"/>
</form>

</body>
</html>

创建视图返回模板form.html

def post(request):
    return render(request,'personal_blog/form.html')

创建模板展示用户form表单输入的数据:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

姓名:{{ uname }}
<br>
密码:{{ upwd }}
<br>
性别:{{ ugender }}
<br>

爱好:
    {% for love in uhobby %}
        {{ love }}
    {% endfor %}


</body>
</html>

创建视图接收post请求参数调用 post1.html模板,将post请求的参数传给模板渲染

def post1(request):

    p_dict = request.POST   # 获取post请求参数
    uname = p_dict.get('uname')
    upwd = p_dict.get('upwd')
    ugender = p_dict.get('ugender')
    uhobby = p_dict.getlist('uhobby')  # 爱好是一键多值,使用getlist方法获取所有爱好
    context = {'uname':uname,'upwd':upwd,'ugender':ugender,'uhobby':uhobby}

    return render(request,'personal_blog/post1.html',context=context)

配置url

    url(r'^post/$', views.post),
    url(r'^post1/$', views.post1),

填写表单提交后效果如下:

HttpResponse对象

与由Django自动创建的HttpRequest 对象相比,HttpResponse 对象由程序员创建.创建的每个视图负责初始化实例,填充并返回一个 HttpResponse或者子对象.

HttpResponse 类是在django.http模块中定义的。

1、HttpResponse 属性

  • content:表示返回的内容
  • charset:表示response采用的编码字符集,默认为utf-8
  • status_code:返回的HTTP响应状态码
  • content-type:指定返回数据的的MIME类型,默认为'text/html'

修改响应状态码:

def resp(request):

    response = HttpResponse(content='hhh',status=300,)
    print(response.status_code)

    return response

配置url

url(r'^resp/$', views.resp),

2、HttpResponse 方法

  • set_cookie:设置Cookie信息
  • write:向响应体中写数据

  • Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。

  • Cookie是由服务器端生成,发送给一般是浏览器,浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。
  • Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否合法用户以及是否需要重新登录等。
  • Cookie基于域名安全,不同域名的Cookie是不能互相访问的,
  • 浏览器每次请求时都会将cookie发给服务器

典型应用:网站的广告推送,购物车,记住用户名

cookie是网站以键值对格式存储在浏览器中的一段纯文本信息。

set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False)
  • max_age 以秒为单位,如果Cookie 只应该持续客户端浏览器的会话时长则应该为None(默认值)。
  • expires 应该是一个 UTC "Wdy, DD-Mon-YY HH:MM:SS GMT" 格式的字符串,或者一个datetime.datetime 对象。如果expires 是一个datetime 对象,则max_age 会通过计算得到。
  • max_age与expires二选一
  • 如果不指定过期时间,则关闭浏览器过期
  • 如果你想设置一个跨域的Cookie,请使用domain 参数。例如,domain=".lawrence.com" 将设置一个www.lawrence.com、blogs.lawrence.com 和calendars.lawrence.com 都可读的Cookie。否则,Cookie 将只能被设置它的域读取。
  • 如果你想阻止客服端的JavaScript 访问Cookie,可以设置httponly=True。

delete_cookie(key):删除指定的key的Cookie,如果key不存在则什么也不发生

3、设置cookie

创建视图设置cookie

def set_cookie(request):
    response = HttpResponse("设置cookie")
    response.set_cookie('username', 'admin')
    response.set_cookie('password','123456',max_age=60*60*12) # 设置超时时间 60*60*12秒
    return response

创建视图删除cookie

def delete_cookie(request):
    response = HttpResponse('删除cookie')
    response.delete_cookie('password') # 删除键为password 的cookie
    return response

创建视图获取cookie

def get_cookie(request):
    cookie = request.COOKIES  # 获取cookie
    username = cookie.get('username')
    password = cookie.get('password')
    response = HttpResponse('%s  %s'%(username,password))  # 将获取到的cookie返回
    return response

配置url

    url(r'^set_cookie/$', views.set_cookie),
    url(r'^delete_cookie/$', views.delete_cookie),
    url(r'^get_cookie/$', views.get_cookie),

在浏览器中查看cookie 以火狐为例子: 在浏览器中按F12,或者在设置找到开发者管理工具,由于没有写入cookie所有cookie列表为空

访问: http://127.0.0.1:8000/blog/set_cookie/ 设置cookie

访问:http://127.0.0.1:8000/blog/get_cookie/

4、子类 JsonResponse

在web开发中,前端会经常要求后台传输数据时返回json格式的数据。 JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。 json数据是类似字典的格式,键/值对使用 "" 。 比如:{"username":"admin","password":"123456"}

django 使用JsonResponse对象返回json数据

JsonResponse继承自HttpResponse对象,被定义在django.http模块中 接收字典作为参数

from django.http import JsonResponse

def json(request):
    return JsonResponse({'username':'admin'})

配置url

url(r'^json/$', views.json),

访问视图:http://127.0.0.1:8000/blog/json/ ,可以看到返回的是json数据

所以在以后web开发中,如果前端页面需要json格式的数据,需要先将数据构造成python字典对象,然后使用JsonRespouse 返回。

5、子类 HttpResponseRedirect 重定向对象

当一个视图处理完成后,不需要向客户端呈现数据,而是跳转到其他页面,比如用户登录成功之后,需要跳转到登录成功后的页面。此时就需要模拟一个用户请求的效果,从一个视图转到另外一个视图,就称为重定向。

django中提供 HttpResponseRedirect 对象实现重定向功能。HttpResponseRedirect 定义在django.http模块中

要使用HttpResponseRedirect 需要先导入

from django.http import HttpResponseRedirect

下面我们用模拟登录做示例: 定义一个登录页面模板。当登录成功之后跳转到博客首页。如果账号密码校验错误,返回原来的登录页面。 登录模板 login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" action="/blog/login/">
    用户名:<input name="uname" value="{{ uname }}">
    密码: <input name="upwd" value="{{ upwd }}">
    <input type="submit" value="登录">
</form>

<span style="color: red">{{ iserror }}</span>

</body>
</html>

处理用户登录视图:

def login(request):

    # 帐号密码
    username = '老狗'
    password = '123456'

    if request.method == 'GET':
        return render(request, 'personal_blog/login.html')
    elif request.method == 'POST':
        dict = request.POST  # 获取POST请求参数
        uname = dict.get('uname') #获取用户名
        upwd = dict.get('upwd')  # 获取用户输入密码
        if username == uname and upwd ==password:
            # 如果接收到的帐号密码与初始设在的一样那么表示登录成功需要跳转到博客首页
            # 重定向
            return HttpResponseRedirect('/blog/index/')

        else:
            #帐号或密码错误,重新返回登录模板,并将用户输入的账号密码当参数传递回去。
            context = {'iserror': '帐号或密码错误',
                       'uname':uname,
                        'upwd':upwd
                       }
            return render(request, 'personal_blog/login.html',context=context)

重定向状态码:302

重定向简写函数 redirect redirect 被定义在django.shortcuts中

from django.shortcuts import redirect

将HttpResponseRedirect 修改成redirect

 return redirect('/blog/index/')

redirect 用法跟HttpResponseRedirect 一样

6、URLconf name在视图中的应用

URLconf中的name参数也可以在视图中使用,在重定向的时候,需要用的url,那么name参数也可以用在重定向的。 reverse (viewname, args=(参数,)) viewname是您要解析的网址路径。args是参数元祖

导入 reverse 函数

from django.core.urlresolvers import reverse

例如上面重定向的url可以不写固定url,利用name参数动态解析

def login(request):

    # 初始帐号密码
    username = '老狗'
    password = '123456'

    if request.method == 'GET':
        return render(request, 'personal_blog/login.html')
    elif request.method == 'POST':
        dict = request.POST  # 获取POST请求参数
        uname = dict.get('uname') #获取用户名
        upwd = dict.get('upwd')  # 获取用户输入密码
        if username == uname and upwd ==password:
            '''如果接收到的帐号密码与初始设在的一样那么表示登录成功需要跳转到博客首页'''
            return HttpResponseRedirect(reverse('index'))

        else:
            # 帐号或密码错误,重新返回登录模板,并将用户输入的账号密码当参数传递回去。
            context = {'iserror': '帐号或密码错误',
                       'uname':uname,
                        'upwd':upwd
                       }
            return render(request, 'personal_blog/login.html',context=context)

Session

对于敏感、重要的信息不能存在浏览器,也就不能使用cookie,如用户名、余额、等级、验证码等信息 如何确保数据的安全性呢,那就是将数据保存在服务端。这就利用到session技术。

Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。

1、启用Session

django项目默认启用Session: settings.py文件,在项MIDDLEWARE_CLASSES中启用Session中间件

存储方式: 设置SESSION_ENGINE项指定Session数据存储的方式,可以存储在数据库、缓存、Redis等 存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式

SESSION_ENGINE='django.contrib.sessions.backends.db'

存储在缓存中:存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快

SESSION_ENGINE='django.contrib.sessions.backends.cache'

混合存储:优先从本机内存中存取,如果没有则从数据库中存取

SESSION_ENGINE='django.contrib.sessions.backends.cached_db'

如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用

django中默认的session保存在数据库,迁移之后会默认生成django_session表

Session包括三个数据:键,值,过期时间

Session依赖于Cookie

所有请求者的Session都会存储在服务器中,启用Session后,会在Cookie中存储一个sessionid,每次请求时浏览器都会将这个数据发给服务器,服务器在接收到sessionid后,会根据这个值找出这个请求者的Session。 所以如果想要使用session,浏览器必须支持cookie,一般浏览器都会开启cookie,除非是人为关闭。

2、Session 方法

通过HttpRequest对象的Session属性进行会话的读写操作 以键值对的格式写会话 设置session值:

request.session['键']=值

根据键读取值

request.session.get('键',默认值)

清除所有会话,删除值部分

request.session.clear()

清除会话数据,删除会话的整条数据

request.session.flush()

删除会话中的指定键及值,在存储中只删除某个键及对应的值

del request.session['键']

设置会话的超时时间,如果没有指定过期时间则两周后过期

request.session.set_expiry(value)

如果value是一个整数,会话将在value秒没有活动后过期 如果value为0,那么用户会话的session将在用户的浏览器关闭时过期 如果value为None,那么会话永不过期

在视图中设置session。

def set_session(request):
    request.session['h1'] = 'python'  # 以键值对的方式设置session
    request.session['h2'] = 'django'  
    request.session['h3'] = 'linux'
    request.session['h4'] = 'java'
    request.session.set_expiry(60*60)  # 设置过期时间为60*60秒
    return HttpResponse('session')

配置 url

 url(r'^set_session/$', views.set_session),

浏览器访问:http://127.0.0.1:8000/blog/set_session/

可以看到cookie中保存的sessionid跟数据库中保存的session_key有一条相同的。就是这个回话保存在数据库中的session

数据库中保存的session采用base64编码,解码之后可以看到session保存的键值对:

获取session值

def get_session(request):
    a = request.session.get('h1')
    return HttpResponse(a)

删除session

def delete_session(request):
    # del request.session['h1']  # 删除键为h1的值
    # request.session.clear()   # 清除session所有值,但保留key
    request.session.flush()   # 删除整个session
    return HttpResponse('ok')

配置url

url(r'^delete_session/$', views.delete_session),

基于类的通用视图

在开发网站的过程中,有一些视图函数虽然处理的对象不同,但是其大致的代码逻辑是一样的。比如一个博客和一个论坛,通常其首页都是展示一系列的文章列表或者帖子列表。对处理首页的视图函数来说,虽然其处理的对象一个是文章,另一个是帖子,但是其处理的过程是非常类似的。先从数据库取出文章或者帖子列表,然后将这些数据传递给模板并渲染模板。

Django 把这些相同的逻辑代码抽取了出来,写成了一系列的通用视图函数,即基于类的通用视图。

类视图被封装在 django.views.generic

先导入类视图

from django.views.generic import ListView

1、列表视图 ListView

在我们的博客应用中,index视图函数是从数据库中获取文章(Post)列表数据的: 博客首页的函数视图

def index(request):
    post_list = Post.post_object.all()
    return render(request, 'personal_blog/index.html', context={"post_list": post_list})

将 index 视图函数改写为类视图

class IndexView(ListView):
    model = Post
    template_name = 'personal_blog/index.html'
    context_object_name = 'post_list'

要写一个类视图,首先要继承django提供的类视图,这里是获取POST文章列表,继承ListView列表视图。列表视图 ListView 就是用来获取模型类的列表数据,所以我们首页需要继承ListView.

  • model。将 model 指定为 Post,告诉 Django 我要获取的模型是 Post。
  • template_name。指定这个视图渲染的模板是 'personal_blog/index.html'。
  • context_object_name。指定获取的模型列表数据保存的变量名。这个变量会被传递给模板,相当于函数视图中的context参数。

直接看代码好像不好理解下面详细分析下:

1、index 视图函数首先通过 Post.objects.all() 从数据库中获取文章(Post)列表数据,并将其保存到 post_list 变量中。在类视图中这个过程 ListView 已经帮我们做了。我们只需告诉 ListView 去数据库获取的模型是 Post,即指定 model = Post

2、将获得的模型数据列表保存到 post_list 里,即指定 context_object_name = 'post_list'。

3、然后渲染 'personal_blog/index.html' 模板文件,index 视图函数中使用 render 函数。但这个过程 ListView 已经帮我们做了,我们只需指定渲染哪个模板即可。

配置url,函数视图的url跟类视图url有所区别 , IndexView 是一个类,不能直接替代 index 函数需要先将类视图转换成函数视图,调用类视图的 as_view() 方法.

    # url(r'^index/$',views.index,name='index'), # 函数视图
    url(r'^index/$', views.IndexView.as_view(), name='index'),  #类视图

配置好后,访问首页效果跟函数视图一样。

重写detail函数视图为类视图。

class MyDetailView(IndexView):

    template_name = 'personal_blog/detail.html'
    context_object_name = 'post'

    def get_queryset(self):
        return super(MyDetailView, self).get_queryset().get(id=self.args[0])

配置url

    # url(r'^detail/(\d+)/',views.detail,name='detail'),
    url(r'^detail/(\d+)/',views.MyDetailView.as_view(),name='detail'),

详细页视图函数的功能也是从数据库中获取文章列表数据,不过其和 index 视图函数不同的是,它获取的是某一篇文章的详细信息。跟index视图区别就在于,需要从url中提取文章id参数。

参数说明: 在类视图中,从URL捕获的命名组参数值保存在实例的kwargs属性(是一个字典)里,非命名参数值按位置顺序保存在实例的args属性(是一个列表)里。 所以在类视图中要获取文章id使用 self.args[0]

调用父类的 get_queryset 方法获得全部文章列表在使用get方法获取指定id的文章。

由于模板不一样所以要覆盖 template_name = 'personal_blog/detail.html'

模板参数不一样所以要覆盖 context_object_name = 'post'

使用类视图可以减少重复代码,因为类视图可以继承,我们这个例子代码量比较少看不出太大效果。

2、添加额外的上下文

get_context_data 上下文管理方法

通常来说,get_context_data会将当前类中的上下文数据,合并父类中的上下文数据,传给模板。但前提是你调用了父类的get_context_data方法。

class MyDetailView(IndexView):
    template_name = 'personal_blog/detail.html'
    context_object_name = 'post'

    def get_queryset(self):
        return super(MyDetailView, self).get_queryset().get(id=self.args[0])

    def get_context_data(self, **kwargs):
        # 调用父类的get_context_data 方法
        context = super(MyDetailView, self).get_context_data(**kwargs)
        # 添加 上下文
        context['username'] = '盖伦'
        return context   # 返回上下文

3、处理表单基于类的视图

在讲重定向的时候,讲过一个模拟登录的例子。其中判断是提交数据,还是请求登录页面采用的是if else 逻辑判断,使用类视图会让登录逻辑控制更为简洁易读。

使用基于类的视图处理表单重写模拟登录的案例:

from django.views.generic import View
class LoginView(View):
    # 初始帐号密码
    username = '老狗'
    password = '123456'
    template_name = 'personal_blog/login.html'

    # get请求将调用get方法
    def get(self, request, *args, **kwargs):
        return render(request, self.template_name)

    # post请求将调用post方法
    def post(self, request, *args, **kwargs):

        dict = request.POST  # 获取用户提交的post参数
        uname = dict.get('uname')
        upwd = dict.get('upwd')

        if self.username == uname and upwd == self.password:
            # 登录成功
            return redirect('/blog/index/')
        else:
            # 登录失败
            context = {'iserror': '帐号或密码错误',
                       'uname': uname,
                       'upwd': upwd
                       }

            return render(request, self.template_name, context=context)
Iyoyo电子书 一本集作者多年开发经验的python电子书 all right reserved,powered by Gitbook文件修订时间: 2022年 10:28:28