Django通用视图
在开发网站的过程中,有一些视图函数虽然处理的对象不同,但是其大致的代码逻辑是一样的。比如一个博客和一个论坛,通常其首页都是展示一系列的文章列表或者帖子列表。对处理首页的视图函数来说,虽然其处理的对象一个是文章,另一个是帖子,但是其处理的过程是非常类似的。
于是Django使用通用视图,抽象出一些在视图开发中常用的代码和模式,这样就可以在无需编写大量代码的情况下,快速编写出常用的数据视图。
基本视图
View
引入
from django.views.generic.base import View
介绍
这个类是通用类的基类,其他类都是继承自这个类。
当我们继承这个类,构造基于类的视图时,只需要实现不同request请求的处理函数。例如实现get(self, request, *args, **kwargs)
处理来自GET方法的请求、实现post(self, request, *args, **kwargs)
处理来自POST方法的请求。函数名为请求名的小写。
类函数
只列举部分个人认为比较重要的函数
dispatch
#分发函数,将请求根据其方法类型分发到不同处理函数(handler) def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
as_view
#在URLconf处调用,请求入口函数,在函数内部调用`dispatch`将请求分发处理 @classonlymethod def as_view(cls, **initkwargs): ... def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) ... return view
当构造基于类的视图时,基本就是靠以上两个函数来将请求分发到我们实现的处理函数里的。
示例
# views.py
from django.http import HttpResponse
from django.views.generic import View
class MyView(View):
#实现get函数处理来自GET方法的请求
def get(self, request, *args, **kwargs):
return HttpResponse('Hello, World!')
# urls.py
from django.urls import path
from myapp.views import MyView
urlpatterns = [
path('mine/', MyView.as_view(), name='my-view'),
]
TemplateView
引入
from django.views.generic.base import TemplateView
介绍
渲染给定的模板文件。
类图
View类提供了as_view()类方法,它返回一个内方法view(),在这个方法中,做的事情就是dispatch的事情,根据请求的方法,调用相应的方法去处理请求,如果你发送了一个GET请求,那么在view()方法中就会分发到get()方法中去处理。
ContextMixin类则只是实现了一个方法,get_context_data(),这是为在渲染template的时候,提供了一个默认的context,一般子类都会重写这个方法的。
TemplateResponseMixin类,就是真正干事的类了,它在render_to_response()方法中,返回一个TemplateResponse对象,template用的就是类属性template_name指定的。
然后TemplateView继承上面的三个类,实现了get()方法。
类函数
get_template_names
,#通过`template_name`属性获取模板文件名称 def get_template_names(self): """ Return a list of template names to be used for the request. Must return a list. May not be called if render_to_response() is overridden. """ if self.template_name is None: raise ImproperlyConfigured( "TemplateResponseMixin requires either a definition of " "'template_name' or an implementation of 'get_template_names()'") else: return [self.template_name]
get_context_data
#传递额外参数到模板,一般继承TemplateView时会重载这个函数,以传递自定义参数到模板中 def get_context_data(self, **kwargs): if 'view' not in kwargs: kwargs['view'] = self if self.extra_context is not None: kwargs.update(self.extra_context) return kwargs
示例
省略view层,直接在url层返回结果
#urls.py from django.urls import path from django.views.generic import TemplateView urlpatterns = [ path('', TemplateView.as_view(template_name='home.html'), name='home'), ]
在view层自定义类集成TemplateView类实现
# views.py from django.views.generic.base import TemplateView from articles.models import Article class HomeView(TemplateView): template_name = "home.html" #定义模板名称,必须有。而且template_name这个变量名固定 def get_context_data(self, **kwargs): #传递额外参数到模板,例如模板中可通过变量名latest_articles获取其对象 context = super().get_context_data(**kwargs) context['latest_articles'] = Article.objects.all()[:5] return context
#urls.py from django.urls import path from myapp.views import HomeView urlpatterns = [ path('', HomeView.as_view(), name='home'), ]
<!-- home.html --> <!-- 模板语言会在后续介绍 -->
RedirectView
引入
from django.views.generic.base import RedictView
介绍
重定向到给定的URL。如果给定的URL是None,Django将返回一个HttpResponseGone (410)。
主要:因为Python使用%来实现字符串格式化,所以在url中要包含%的话,必须使用%%。
类图
类变量
permanent = False #是否为永久重定向
url = None #要跳转的网址
pattern_name = None #要跳转的路由地址别名
query_string = False #是否传递GET的参数到跳转网址,True时会传递,默认为 False
类函数
get_redirect_url
#获取跳转地址
def get_redirect_url(self, *args, **kwargs):
if self.url:
url = self.url % kwargs
elif self.pattern_name:
url = reverse(self.pattern_name, args=args, kwargs=kwargs)
else:
return None
args = self.request.META.get('QUERY_STRING', '')
if args and self.query_string:
url = "%s?%s" % (url, args)
return url
#可以看出url和patter_name是不能同时使用的,优先使用url
示例
# views.py
from django.shortcuts import get_object_or_404
from django.views.generic.base import RedirectView
from articles.models import Article
class ArticleCounterRedirectView(RedirectView):
permanent = False
query_string = True
pattern_name = 'article-detail'
def get_redirect_url(self, *args, **kwargs):
article = get_object_or_404(Article, pk=kwargs['pk'])
article.update_counter()
return super().get_redirect_url(*args, **kwargs)
# urls.py
from django.urls import path
from django.views.generic.base import RedirectView
from article.views import ArticleCounterRedirectView
urlpatterns = [
path('counter/<int:pk>/', ArticleCounterRedirectView.as_view(), name='article-counter'),
path('go-to-django/', RedirectView.as_view(url='https://djangoproject.com'), name='go-to-django'),
]