视图函数,简称视图,本质上是一个简单的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
,你可以取别的名字,但这不符合潜规则,最好不要那么做。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的情况。
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后:
django.conf.urls.handler404
的值,默认为django.views.defaults.page_not_found()
视图page_not_found()
视图上面的流程就给我们留下了两个自定义404页面的钩子:
一个是自定义处理视图,一个是自定义展示的404页面。
自定义的404.html页面应当位于模板引擎可以搜索到的路径。
当Django找不到与请求匹配的URL时,或者当抛出一个异常时,将调用一个错误处理视图。
Django默认的自带的错误视图包括400、403、404和500,分别表示请求错误、拒绝服务、页面不存在和服务器错误。
它们对应的URL分别是:
每个URL又分别对应下面的内置视图:
我们可以在根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(request, template_name, context=None, content_type=None, status=None, using=None)
结合一个给定的模板和一个给定的上下文字典,返回一个渲染后的HttpResponse对象。
必需参数:
可选参数:
locals()
方法,可以方便地将函数作用域内的所有变量一次性添加进去。DEFAULT_CONTENT_TYPE
设置的值,也就是'text/html'
。范例:
下面的例子将渲染模板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(to, args, permanent=False, *kwargs)
根据传递进来的url参数,返回HttpResponseRedirect。
参数to可以是:
get_absolute_url()
函数,反向解析出目的urlblog:index
默认情况下是临时重定向,如果设置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')
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(klass, args, *kwargs)
这个方法,非常重要,请一定熟记。
查询单个对象,如果失败,直接return一个404页面。
Model.objects.get(...),如果失败,弹出DoesNotExist异常,但不涉及如何return响应的工作。
常用于查询某个对象,找到了则进行下一步处理,如果未找到则给用户返回404页面。
此方法有一个异步版本,你们知道的,以a开头,aget_object_or_404
。
必需参数:
范例:
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(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开始,这一系列装饰器支持异步视图。
此装饰器位于 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方法名必须是大写字符串。
上面的狭隘版本,只允许GET请求访问。位于 django.views.decorators.http
模块。
只允许POST请求访问。位于 django.views.decorators.http
模块。
只允许安全的请求类型,也就是GET和HEAD访问。位于 django.views.decorators.http
模块。
对视图的响应内容进行压缩,如果浏览器支持。位于 django.views.decorators.gzip
模块。
用来特别取消自动添加url最后斜杠的机制,这个机制是common
中间件带来的。这在某些场景很有用。
它位于django.views.decorators.common
更多的装饰器如下所示,有兴趣的可以自行研究:
Django开发过程中,除了Python代码、前端静态文件,还有一类媒体文件,比如用户上传的图片、文件等等,统称为MEDIA资源。
这些MEDIA都是有用的资源,我们往往希望根据某个URL,从浏览器上直接获取它们,比如:
这些功能,一般都是在代码上线后,通过类似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, }), ]
然后:
settings.DEBUG=True
MEDIA_ROOT = BASE_DIR / 'media/' MEDIA_URL = '/media/'
media
文件夹,将你的MEDIA资源拷贝进去。127.0.0.1:8000/media/logo.jpg
的地址可以看到图片127.0.0.1:8000/media/blog.md
的地址会自动下载blog.md文件实际上,我们默认启动的开发服务器一样可以提供这个媒体服务功能。为什么?因为runserver命令隐含了对serve的调用!
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()
讲解得很到位。