视图函数及快捷方式

阅读: 41651     评论:7

视图函数,简称视图,本质上是一个简单的Python函数,它接受Web请求并且返回Web响应。

响应的内容可以是HTML网页、重定向、404错误,XML文档或图像等任何东西。但是,无论视图本身是个什么处理逻辑,最好都返回某种响应。

视图函数的代码写在哪里也无所谓,只要它在你的Python目录下面。但是通常我们约定将视图放置在项目或应用程序目录中的名为views.py的文件中。

一、简单的视图

下面是一个返回当前日期和时间作为HTML文档的视图:

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

让我们逐行分析一下上面的代码:

  • 首先,从django.http模块导入了HttpResponse类,以及Python的datetime库。
  • 接着,我们定义了current_datetime视图函数。
  • 每个视图函数都接收一个HttpRequest对象作为第一位置参数,一般取名为request,你可以取别的名字,但这不符合潜规则,最好不要那么做。
  • 视图函数的名称没有强制规则,但尽量不要和Python及Django内置的各种名称重名,并且尽量精确地反映出它的功能,比如这里的current_datetime
  • 该视图返回一个HttpResponse对象,其中包含生成的HTML页面。

二、返回错误

在Django中返回HTTP错误代码是非常简单的。

HttpResponse的许多子类对应着除了200(代表“OK”)以外的一些常用的HTTP状态码。

为了标示一个错误,可以直接返回那些子类中的一个实例,而不是普通的HttpResponse。像下面这样:

from django.http import HttpResponse, HttpResponseNotFound

def my_view(request):
    # ...
    if foo:
        return HttpResponseNotFound('<h1>Page not found</h1>')
    else:
        return HttpResponse('<h1>Page was found</h1>')

Django为404错误提供了一个特化的子类HttpResponseNotFound。由于一些状态码不太常用,所以不是每个状态码都有一个特化的子类。

也可以向HttpResponse的构造器传递HTTP状态码,来创建你想要的任何状态码的返回类。 像下面这样:

from django.http import HttpResponse

def my_view(request):
    # ...

    # Return a "created" (201) response code.
    return HttpResponse(status=201)

关键是在返回中提供status=201参数。别的什么303之类的错误都可以参照上面的例子。

三、Http404异常

class django.http.Http404

当你返回错误,例如 HttpResponseNotFound ,你需要定义错误页面的 HTML 。

return HttpResponseNotFound('<h1>Page not found</h1>')

为了方便,Django 内置了 Http404 异常。(没有Http400、Http403等,只有这一个)

如果你在视图的任何地方引发了 Http404 ,Django 会捕捉到它并且返回标准的错误页面,连同 HTTP 错误代码 404 。

from django.http import Http404
from django.shortcuts import render
from polls.models import Poll

def detail(request, poll_id):
    try:
        p = Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404("Poll does not exist")  # 注意是raise,不是return
    return render(request, 'polls/detail.html', {'poll': p})

为了在Django返回404时显示自定义的HTML,可以创建一个名为404.html的HTML模板,并将其放置在模板树的顶层。

只有当DEBUG设置为False时,此模板才会被自动使用。DEBUG为True表示开发模式,Django会展示详细的错误信息页面,而不是针对性的404页面。

实际上,当你raise了Http404后:

  1. Django会首先读取django.conf.urls.handler404的值,默认为django.views.defaults.page_not_found()视图
  2. 执行page_not_found()视图
  3. 判断是否自定义了404.html,如果有,输出该HTML文件
  4. 如果没有,输出默认的404提示信息

上面的流程就给我们留下了两个自定义404页面的钩子:

  • 第一个是在urls中重新指定handler404的值,也就是用哪个视图来处理404
  • 第二个是在page_not_found视图中,使用自定义的404.html

一个是自定义处理视图,一个是自定义展示的404页面。

自定义的404.html页面应当位于模板引擎可以搜索到的路径。

四、自定义各种错误页面

接着上面的内容。

当Django找不到与请求匹配的URL时,或者当抛出一个异常时,将调用一个错误处理视图。Django默认的自带的错误视图包括400、403、404和500,分别表示请求错误、拒绝服务、页面不存在和服务器错误。它们分别位于:

  • handler400 —— django.conf.urls.handler400。
  • handler403 —— django.conf.urls.handler403。
  • handler404 —— django.conf.urls.handler404。
  • handler500 —— django.conf.urls.handler500。

它们又分别对应下面的内置视图:

  • handler400 —— django.views.defaults.bad_request()
  • handler403 —— django.views.defaults.permission_denied()
  • handler404 —— django.views.defaults.page_not_found()
  • handler500 —— django.views.defaults.server_error()

我们可以在根URLconf中设置它们。在其它app中的二级URLconf中设置这些变量无效。

Django有内置的HTML模版,用于返回错误页面给用户,但是这些403,404页面实在丑陋,通常我们都自定义错误页面。

首先,在根URLconf中额外增加下面的条目,并导入views模块:

from django.contrib import admin
from django.urls import path
from app import views

urlpatterns = [
    path('admin/', admin.site.urls),
]

# 增加的条目
handler400 = views.bad_request
handler403 = views.permission_denied
handler404 = views.page_not_found
handler500 = views.error

然后在,app/views.py文件中增加四个处理视图:

from django.shortcuts import render
from django.views.decorators.csrf import requires_csrf_token

@requires_csrf_token
def bad_request(request, exception):
    return render(request, '400.html')

@requires_csrf_token
def permission_denied(request, exception):
    return render(request, '403.html')

@requires_csrf_token
def page_not_found(request, exception):
    return render(request, '404.html')

@requires_csrf_token
def error(request):
    return render(request, '500.html')

再根据自己的需求,创建对应的400、403、404、500.html四个页面文件,就可以了(要注意好模板文件的引用方式,视图的放置位置等等)。

只有当DEBUG设置为False时,这些错误视图才会被自动使用。DEBUG为True表示开发模式,Django会展示详细的错误信息页面,而不是针对性的错误页面。

五、异步视图

Django3.1开始支持异步视图函数。

编写异步视图,只需要用Python的async def关键字语法。Django将自动地探测和运行视图在一个异步上下文环境中。

为了让异步视图发挥它的性能优势,你需要启动一个基于ASGI的异步服务器。

下面是一个异步视图的例子:

import datetime
from django.http import HttpResponse

async def current_datetime(request):
    now = datetime.datetime.now()
    html = '<html><body>It is now %s.</body></html>' % now
    return HttpResponse(html)

或者如下面的例子:

async def my_view(request):
    await asyncio.sleep(0.5)
    return HttpResponse('Welcome to visit https//www.liujiangblog.com for Django course')

简要说明:

  • 异步功能同时支持WSGI和ASGI模式
  • 在WSGI模式下,使用异步功能会有性能损失
  • 可以混用异步/同步视图或中间件,Django会自动处理其中的上下文
  • 建议主要使用同步模式,在有需求的场景才使用异步功能。
  • Django的ORM系统、缓存层和其它的一些需要进行长时间网络/IO调用的代码依然不支持异步访问,在未来的版本中将逐步支持。
  • 异步功能不会影响同步代码的执行速度,也不会对已有项目产生明显的影响。

六、内置的快捷方法

Django在django.shortcuts模块中,为我们提供了很多快捷方便的类和方法,它们都很重要,使用频率很高。

render()

render(request, template_name, context=None, content_type=None, status=None, using=None)

结合一个给定的模板和一个给定的上下文字典,返回一个渲染后的HttpResponse对象。

必需参数:

  • request:视图函数处理的当前请求,封装了请求头的所有数据,其实就是视图参数request。
  • template_name:要使用的模板的完整名称或者模板名称的列表。如果是一个列表,将使用其中能够查找到的第一个模板。

可选参数:

  • context:添加到模板上下文的一个数据字典。默认是一个空字典。可以将认可需要提供给模板的数据以字典的格式添加进去。这里有个小技巧,使用Python内置的locals()方法,可以方便地将函数作用域内的所有变量一次性添加进去。
  • content_type:用于生成的文档的MIME类型。 默认为DEFAULT_CONTENT_TYPE设置的值,也就是'text/html'
  • status:响应的状态代码。 默认为200。
  • using:用于加载模板使用的模板引擎的NAME。

范例:

下面的例子将渲染模板myapp/index.html,MIME类型为application/xhtml+xml

from django.shortcuts import render

def my_view(request):
    # View code here...
    return render(request, 'myapp/index.html', {
        'foo': 'bar',
    }, content_type='application/xhtml+xml')

这个示例等同于:

from django.http import HttpResponse
from django.template import loader

def my_view(request):
    # View code here...
    t = loader.get_template('myapp/index.html')
    c = {'foo': 'bar'}
    return HttpResponse(t.render(c, request), content_type='application/xhtml+xml')

redirect()

redirect(to, args, permanent=False, *kwargs)

根据传递进来的url参数,返回HttpResponseRedirect。

参数to可以是:

  • 一个模型实例:将调用模型的get_absolute_url()函数,反向解析出目的url;
  • URL的name名称:可能带有参数:reverse()将用于反向解析url;
  • 一个绝对的或相对的URL:将原封不动的作为重定向的目标位置。

默认情况下是临时重定向,如果设置permanent=True将永久重定向。

范例:

1.调用对象的get_absolute_url()方法来重定向URL:

from django.shortcuts import redirect

def my_view(request):
    ...
    obj = MyModel.objects.get(...)
    return redirect(obj)

2.传递URL的name名称,内部会自动使用reverse()方法反向解析url:

def my_view(request):
    ...
    return redirect('some-view-name', foo='bar')
  1. 重定向到硬编码的URL:
def my_view(request):
    ...
    return redirect('/some/url/')
  1. 重定向到一个完整的URL:
def my_view(request):
    ...
    return redirect('https://www.liujiangblog.com/')

#或者
def my_view(request):
    ...
    return redirect('/some/url/')

所有上述形式都接受permanent参数,如果设置为True,将返回永久重定向:

def my_view(request):
    ...
    obj = MyModel.objects.get(...)
    return redirect(obj, permanent=True)

get_object_or_404()

get_object_or_404(klass, args, *kwargs)

这个方法,非常有用,请一定熟记。

常用于查询某个对象,找到了则进行下一步处理,如果未找到则给用户返回404页面。

在后台,Django其实是调用了模型管理器的get()方法,只会返回一个对象。不同的是,如果get()发生异常,会引发Http404异常,从而返回404页面,而不是模型的DoesNotExist异常。

必需参数

  • klass:要获取的对象的Model类名或者Queryset等;
  • **kwargs:查询的参数,格式应该可以被get()接受。

范例:

1.从MyModel中使用主键1来获取对象:

from django.shortcuts import get_object_or_404

def my_view(request):
    obj = get_object_or_404(MyModel, pk=1)

这个示例等同于:

from django.http import Http404

def my_view(request):
    try:
        obj = MyModel.objects.get(pk=1)
    except MyModel.DoesNotExist:
        raise Http404("No MyModel matches the given query.")

2.除了传递Model名称,还可以传递一个QuerySet实例:

queryset = Book.objects.filter(title__startswith='M')
get_object_or_404(queryset, pk=1)

上面的示例不够简洁,它等同于:

get_object_or_404(Book, title__startswith='M', pk=1)

3.还可以使用Manager。 如果你自定义了管理器,这将很有用:

get_object_or_404(Book.dahl_objects, title='Matilda')

4.还可以使用related managers:

author = Author.objects.get(name='Roald Dahl')
get_object_or_404(author.book_set, title='Matilda')

与get()一样,如果找到多个对象将引发一个MultipleObjectsReturned异常。

get_list_or_404()

get_list_or_404(klass, args, *kwargs)

这其实就是get_object_or_404多值获取版本。

在后台,返回一个给定模型管理器上filter()的结果,并将结果映射为一个列表,如果结果为空则弹出Http404异常。

必需参数

  • klass:获取该列表的一个Model、Manager或QuerySet实例。
  • **kwargs:查询的参数,格式应该可以被filter()接受。

范例:

下面的示例从MyModel中获取所有发布出来的对象:

from django.shortcuts import get_list_or_404

def my_view(request):
    my_objects = get_list_or_404(MyModel, published=True)

这个示例等同于:

from django.http import Http404

def my_view(request):
    my_objects = list(MyModel.objects.filter(published=True))
    if not my_objects:
        raise Http404("No MyModel matches the given query.")

七、视图装饰器

Django内置了一些装饰器,用来对视图函数进行一些控制。

require_http_methods()

此装饰器位于 django.views.decorators.http模块,用于限制可以访问该视图的HTTP方法。

如果请求的HTTP方法,不是指定的方法之一,则返回django.http.HttpResponseNotAllowed响应。

看例子:

from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])             # 注意参数的提供方式
def my_view(request):
    # 我们决定只有GET或者POST请求可以访问这个视图
    # ...
    pass

参数必须是大写字符串。

require_GET()

上面的狭隘版本,只允许GET请求访问。位于 django.views.decorators.http模块。

require_POST()

只允许POST请求访问。位于 django.views.decorators.http模块。

require_safe()

只允许安全的请求类型,也就是GET和HEAD访问。位于 django.views.decorators.http模块。

gzip_page()

对视图的响应内容进行压缩,如果浏览器支持。位于 django.views.decorators.gzip模块。

更多的装饰器如下所示,有兴趣的可以自行研究:

  • condition(etag_func=None, last_modified_func=None)
  • etag(etag_func)
  • last_modified(last_modified_func)
  • vary_on_cookie(func)
  • vary_on_headers(*headers)
  • cache_control()
  • never_cache(view_func)

八、serve()视图

Django开发过程中,出了Python代码、前端静态文件,还有一类媒体文件,比如用户上传的图片、文件等等,统称为MEDIA。

这些MEDIA都是有用的资源,我们往往希望根据某个URL,从浏览器上直接获取它们,比如:

  • 访问https://www.liujiangblog.com/logo.jpg,浏览器会显示logo图片
  • 访问https://www.liujiangblog.com/document.doc,浏览器会自动下载document文档

这些功能,一般都是在代码上线后,通过类似Ngnix的Web服务器代理实现的。

为了方便在开发过程中,对MEDIA资源的使用和测试,Django内置了一个serve()视图,帮我们实现了同样的功能。

但是要谨记:serve()视图只能用于开发环境!

使用步骤:

首先,在根路由urls中添加下面的代码:

from django.conf import settings
from django.urls import re_path
from django.views.static import serve              # 以上三个导入不能忘

# ... 你原来的URLconf放这里...

if settings.DEBUG:
    urlpatterns += [
        re_path(r'^media/(?P<path>.*)$', serve, {
            'document_root': settings.MEDIA_ROOT,
        }),
    ]

然后:

  1. 确认settings.DEBUG配置项为True,也就是出于开发模式
  2. 在settings中添加下面的配置
MEDIA_URL = '/media/'
MEDIA_ROOT = '/media/'
  1. 如果是windows操作系统,在你的Django项目所在的盘符的根目录下,新建一个media文件夹,将你的MEDIA资源全部拷贝进去,比如c:\media或者d:\media等等。
  2. 启动开发服务器
  3. 访问类似127.0.0.1:8000/media/logo.jpg的地址可以看到图片
  4. 访问类似127.0.0.1:8000/media/blog.md的地址会自动下载blog.md文件

 反向解析和命名空间 异步视图 

评论总数: 7


点击登录后方可评论

context:添加到模板上下文的一个数据字典。默认是一个空字典。可以将认可需要提供给模板的数据以字典的格式添加进去。 “认可”应该是任何。博主写错了哈



在3. redirect()内容中,第2点的return redirect('some-view-name', foo='bar'),其中some-view-name是个视图名,所以不应该是return redirect(reverse('some-view-name'), foo='bar')吗?



redirect 会自动调用 reverse 反向解析出 URL,所以 redirect(reverse(...)) 和直接 redirect(...) 是一样的。



咦,你亲自测试过吗?



亲测,写不写都可以成功重定向



reverse会自动调用的。同样地,传入一个类型(model a)的时候,也会自动调用 a.get_absolute_url()



讲解得很到位。