视图函数,简称视图,本质上是一个简单的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页面。 在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之类的错误都可以参照上面的例子。
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,分别表示请求错误、拒绝服务、页面不存在和服务器错误。它们分别位于:
它们又分别对应下面的内置视图:
我们可以在根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')
简要说明:
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()
函数,反向解析出目的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')
def my_view(request): ... return redirect('/some/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(klass, args, *kwargs)
这个方法,非常有用,请一定熟记。
常用于查询某个对象,找到了则进行下一步处理,如果未找到则给用户返回404页面。
在后台,Django其实是调用了模型管理器的get()方法,只会返回一个对象。不同的是,如果get()发生异常,会引发Http404异常,从而返回404页面,而不是模型的DoesNotExist异常。
必需参数:
**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(klass, args, *kwargs)
这其实就是get_object_or_404
多值获取版本。
在后台,返回一个给定模型管理器上filter()的结果,并将结果映射为一个列表,如果结果为空则弹出Http404异常。
必需参数:
**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内置了一些装饰器,用来对视图函数进行一些控制。
此装饰器位于 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
参数必须是大写字符串。
上面的狭隘版本,只允许GET请求访问。位于 django.views.decorators.http
模块。
只允许POST请求访问。位于 django.views.decorators.http
模块。
只允许安全的请求类型,也就是GET和HEAD访问。位于 django.views.decorators.http
模块。
对视图的响应内容进行压缩,如果浏览器支持。位于 django.views.decorators.gzip
模块。
更多的装饰器如下所示,有兴趣的可以自行研究:
Django开发过程中,出了Python代码、前端静态文件,还有一类媒体文件,比如用户上传的图片、文件等等,统称为MEDIA。
这些MEDIA都是有用的资源,我们往往希望根据某个URL,从浏览器上直接获取它们,比如:
这些功能,一般都是在代码上线后,通过类似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, }), ]
然后:
settings.DEBUG
配置项为True,也就是出于开发模式MEDIA_URL = '/media/' MEDIA_ROOT = '/media/'
media
文件夹,将你的MEDIA资源全部拷贝进去,比如c:\media
或者d:\media
等等。127.0.0.1:8000/media/logo.jpg
的地址可以看到图片127.0.0.1:8000/media/blog.md
的地址会自动下载blog.md文件
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()
讲解得很到位。