视图函数及快捷方式

阅读: 42698     评论: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页面。

这里我们可以测试一下直接返回一个字符串或者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之类的错误都可以参照上面的例子。

这里可以测试一下status设置为403、404、500的情况。

三、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,分别表示请求错误、拒绝服务、页面不存在和服务器错误。

它们对应的URL分别是:

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

每个URL又分别对应下面的内置视图:

  • 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.server_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 server_error(request):
    return render(request, '500.html')

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

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

可以参考下面的例子,写一个测试用例:

from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.test import SimpleTestCase, override_settings
from django.urls import path


def response_error_handler(request, exception=None):
    return HttpResponse("Error handler content", status=403)


def permission_denied_view(request):
    raise PermissionDenied


urlpatterns = [
    path("403/", permission_denied_view),
]

handler403 = response_error_handler


# ROOT_URLCONF must specify the module that contains handler403 = ...
@override_settings(ROOT_URLCONF=__name__)
class CustomErrorHandlerTests(SimpleTestCase):
    def test_handler_renders_template_response(self):
        response = self.client.get("/403/")
        # Make assertions on the response here. For example:
        self.assertContains(response, "Error handler content", status_code=403)

五、内置的快捷方法

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()将name反向解析成具体url,比如blog:index
  • 一个绝对的或相对的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('blog:index', foo='bar')
  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)

这个方法,非常重要,请一定熟记。

查询单个对象,如果失败,直接return一个404页面。

Model.objects.get(...),如果失败,弹出DoesNotExist异常,但不涉及如何return响应的工作。

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

此方法有一个异步版本,你们知道的,以a开头,aget_object_or_404

必需参数

  • klass:要获取的对象的Model类名、manager或者Queryset等
  • args:Q对象
  • kwargs:查询的参数,采用 get() 和 filter() 接受的格式。

范例:

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

from django.shortcuts import get_object_or_404

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

这个示例等同于:

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.")
    pass

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

queryset = Book.objects.filter(title__startswith='M')
obj = get_object_or_404(queryset, pk=1)
# 测试 get_object_or_404(Book.objects.filter(title__startswith='M'), 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异常。

它同样也有一个异步版本aget_list_or_404

参数与get_object_or_404一样。

范例:

下面的示例从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内置了一些装饰器,支持多种 HTTP 特性。

从Django5.0开始,这一系列装饰器支持异步视图。

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):
    # I can assume now that only GET or POST requests make it this far
    # ...
    pass

HTTP方法名必须是大写字符串。

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模块。

no_append_slash()

用来特别取消自动添加url最后斜杠的机制,这个机制是common中间件带来的。这在某些场景很有用。

它位于django.views.decorators.common

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

  • 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(**kwargs)
  • never_cache(view_func)

七、serve()视图

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

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

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

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

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

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

static.serve(request, path, document_root, show_indexes=False)

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

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_ROOT = BASE_DIR / 'media/'
MEDIA_URL = '/media/'
  1. 如果是windows操作系统,在你的Django项目根目录下,新建一个media文件夹,将你的MEDIA资源拷贝进去。
  2. 启动开发服务器
  3. 访问类似127.0.0.1:8000/media/logo.jpg的地址可以看到图片
  4. 访问类似127.0.0.1:8000/media/blog.md的地址会自动下载blog.md文件

实际上,我们默认启动的开发服务器一样可以提供这个媒体服务功能。为什么?因为runserver命令隐含了对serve的调用!


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

评论总数: 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()



讲解得很到位。