django 的类视图和函数视图-杂谈
-
在django很老的版本时候,只有function-based views,但问题是是基于函数的视图太过于简单,很难去拓展,自定义它们,没法达到视图重用的地步。
-
为了解决这个问题,class-based views诞生了。所以,现在的django有基于函数或者基于类这两种视图。
更换网站函数视图为类视图
url和视图关系
一般先打开浏览器,然后在浏览器的地址栏里输入一个网址,也就是URL,然后回车,我们就可以在浏览器里看到这个网址返回的内容。这是我们能看得见的过程,还有一些我们看不见的过程,那就是:当我们在浏览器里输入网址(URL)时,回车,然后浏览器就会向目标网址发送一个HTTP请求,服务器收到请求之后就会给这个请求做出一个响应,这个响应就是把对应的内容通过浏览器渲染出来,呈现给我们看。
这个过程就是请求与响应:
简单来说也就是
浏览器发送请求给服务器服务器对请求进行处理再返回http相应最后返回html文档给前端
表现形式如下:
urlpatterns = [
path(正则表达式, views视图函数,参数,别名),
]
括号里的参数说明:
1、一个正则表达式字符串
2、一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
3、可选的要传递给视图函数的默认参数(字典形式)
4、一个可选的name参数(别名)
比如我想构造三个URL,网站首页(http://www.django.cn/)、新闻(http://www.django.cn/news/)、论坛(http://www.django.cn/bbs/),我们可以这么做。
urlpatterns = [
path('', views.index), #里面留空,代表首页
path('news/',views.news),#news
path('bbs/',views.bbs),#bbs
]
URL就是这么构造的,我们的域名www.django.cn不需要写,完整的URL应该要这么写:path(正则表达式, views视图函数,参数,别名), 里面的正则表达式, views视图函数,是必须要写的,而参数,别名是可选的。我们在有特殊需要的时候才写。关于URL详细介绍和使用方法可以查看文章:路由配置系统URLconf
通过上面我们可以看到,每个URL都对应一个views视图函数名,视图函数名不能相同,否则会报错。视图函数,Django中约定写在APP应用里的views.py文件里。然后在urls.py文件里通过下面的方式导入:
from APP应用名 import views
from APP应用名.vews import 函数名或类名
视图函数是一个简单的Python 函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。这个视图函数代码一般约定是放置在项目或应用程序目录中的名为views.py的文件中。
http请求中产生两个核心对象:
1、http请求---->HttpRequest对象,用户请求相关的所有信息(对象)
2、http响应---->HttpResponse对象,响应字符串
之前我们在欢迎页面这章的时候有操作过一次。我们回顾一下:
首先,打开打开bolg目录下的views.py文件,写一个hello视图函数,在里面输入:
from django.http import HttpResponse
def hello(request):
"""
写一个hello函数,通过request接收URL或者说是http请求信息,
然后给这个请求返回一个HttpResponse对象
"""
return HttpResponse('欢迎使用Django!')
例子里,我们用到的request,就是HttpRequest对象。HttpResponse("欢迎使用Django!"),就是HttpRequest对象,它向http请求响应了一段字符串对象。
我们打开myblog目录下的urls.py文件中先导入视图函数,然后构造一个URL,代码如下:
from blog import views #导入视图函数
urlpatterns = [
...
path('', views.hello), #这个是我们构造的URL
]
代码写完之后,启动项目就可以在浏览器里看到视图函数返回的字符串"欢迎使用Django!"
每一个URL都会对应一个视图函数,当一个用户请求访问Django站点的一个页面时,然后就由Django路由系统(URL配置文件)去决定要执行哪个视图函数使用的算法。
通过URL对应关系匹配 ->找到对应的函数(或者类)->返回字符串(或者读取Html之后返回渲染的字符串)这个过程也就是我们Django请求的生命周期。
视图函数,就是围绕着HttpRequest和HttpResponse这两个对象进行的。
类视图和函数视图的区别
函数视图:
以函数的方式定义的视图称为函数视图,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。
比如我之前网站就是用到的函数视图:
from django.shortcuts import render
from apps.blog.models import Article, Category, Tag
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.http import Http404
from django.conf import settings
categories = Category.objects.all() # 获取全部的分类对象
tags = Tag.objects.all() # 获取全部的标签对象
months = Article.objects.datetimes('pub_time', 'month', order='DESC')
# Create your views here.
def home(request): # 主页
posts = Article.objects.filter(status='p', pub_time__isnull=False) # 获取全部(状态为已发布,发布时间不为空)Article对象
paginator = Paginator(posts, settings.PAGE_NUM) # 每页显示数量
page = request.GET.get('page') # 获取URL中page参数的值
try:
post_list = paginator.page(page)
except PageNotAnInteger:
post_list = paginator.page(1)
except EmptyPage:
post_list = paginator.page(paginator.num_pages)
return render(request, 'home.html', {'post_list': post_list, 'category_list': categories, 'months': months})
def detail(request, id):
try:
post = Article.objects.get(id=str(id))
post.viewed() # 更新浏览次数
tags = post.tags.all()
next_post = post.next_article() # 上一篇文章对象
prev_post = post.prev_article() # 下一篇文章对象
except Article.DoesNotExist:
raise Http404
return render(
request, 'post.html',
{
'post': post,
'tags': tags,
'category_list': categories,
'next_post': next_post,
'prev_post': prev_post,
'months': months
}
)
def search_category(request, id):
posts = Article.objects.filter(category_id=str(id))
category = categories.get(id=str(id))
paginator = Paginator(posts, settings.PAGE_NUM) # 每页显示数量
try:
page = request.GET.get('page') # 获取URL中page参数的值
post_list = paginator.page(page)
except PageNotAnInteger:
post_list = paginator.page(1)
except EmptyPage:
post_list = paginator.page(paginator.num_pages)
return render(request, 'category.html',
{'post_list': post_list,
'category_list': categories,
'category': category,
'months': months
}
)
def search_tag(request, tag):
posts = Article.objects.filter(tags__name__contains=tag)
paginator = Paginator(posts, settings.PAGE_NUM) # 每页显示数量
try:
page = request.GET.get('page') # 获取URL中page参数的值
post_list = paginator.page(page)
except PageNotAnInteger:
post_list = paginator.page(1)
except EmptyPage:
post_list = paginator.page(paginator.num_pages)
return render(request, 'tag.html', {
'post_list': post_list,
'category_list': categories,
'tag': tag,
'months': months
}
)
def archives(request, year, month):
posts = Article.objects.filter(pub_time__year=year, pub_time__month=month).order_by('-pub_time')
paginator = Paginator(posts, settings.PAGE_NUM) # 每页显示数量
try:
page = request.GET.get('page') # 获取URL中page参数的值
post_list = paginator.page(page)
except PageNotAnInteger:
post_list = paginator.page(1)
except EmptyPage:
post_list = paginator.page(paginator.num_pages)
return render(request, 'archive.html', {
'post_list': post_list,
'category_list': categories,
'months': months,
'year_month': year+'年'+month+'月'
}
)
但是后面在写评论、登陆等等功能的时候,感觉业务逻辑处理的很乱,而且我也很迫切地想尽快把网站地功能做的更完善,就搞得几天都摸不着头绪不知道怎么前进。也可能是我一开始写的时候,没有想那么远,后来越做越复杂了,在日后的开放中也感觉类视图的方便。于是我就换成了类视图:
import markdown
import time
from django.views import generic
from django.conf import settings
from django.utils.text import slugify
from django.shortcuts import render, HttpResponse, render_to_response
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import get_object_or_404, get_list_or_404
from .models import Article, Category, Tag
from markdown.extensions.toc import TocExtension # 锚点的拓展
from django.core.cache import cache
# Create your views here.
class IndexView(generic.ListView):
model = Article
template_name = 'index.html'
context_object_name = 'articles'
paginate_by = getattr(settings, 'BASE_PAGE_BY', None)
paginate_orphans = getattr(settings, 'BASE_ORPHANS', 0)
def get_ordering(self):
ordering = super(IndexView, self).get_ordering()
sort = self.kwargs.get('sort')
if sort == 'v':
return ('-views', '-update_date', '-id')
return ordering
class DetailView(generic.DetailView):
model = Article
template_name = 'article.html'
context_object_name = 'article'
def get_object(self):
obj = super(DetailView, self).get_object()
# 设置浏览量增加时间判断,同一篇文章两次浏览超过半小时才重新统计阅览量,作者浏览忽略
u = self.request.user
ses = self.request.session
the_key = 'is_read_{}'.format(obj.id)
is_read_time = ses.get(the_key)
if u != obj.author:
if not is_read_time:
obj.update_views()
ses[the_key] = time.time()
else:
now_time = time.time()
t = now_time - is_read_time
if t > 60 * 30:
obj.update_views()
ses[the_key] = time.time()
# 文章可以使用markdown书写,带格式的文章,像csdn写markdown文章一样
# md = markdown.Markdown(extensions=[
# 'markdown.extensions.extra',
# 'markdown.extensions.codehilite',
# TocExtension(slugify=slugify),
# ])
# obj.body = md.convert(obj.body)
# obj.toc = md.toc
return obj
class CategoryView(generic.ListView):
model = Article
template_name = 'category.html'
context_object_name = 'articles'
paginate_by = getattr(settings, 'BASE_PAGE_BY', None)
paginate_orphans = getattr(settings, 'BASE_ORPHANS', 0)
def get_ordering(self):
ordering = super(CategoryView, self).get_ordering()
sort = self.kwargs.get('sort')
if sort == 'v':
return ('-views', '-update_date', '-id')
return ordering
def get_queryset(self, **kwargs):
queryset = super(CategoryView, self).get_queryset()
cate = get_object_or_404(Category, slug=self.kwargs.get('slug'))
return queryset.filter(category=cate)
def get_context_data(self, **kwargs):
context_data = super(CategoryView, self).get_context_data()
cate = get_object_or_404(Category, slug=self.kwargs.get('slug'))
context_data['search_tag'] = '文章分类'
context_data['search_instance'] = cate
return context_data
def global_setting(request):
return{
"AUTHOR_NAME" : settings.AUTHOR_NAME,
"AUTHOR_DESC" : settings.AUTHOR_DESC,
"AUTHOR_EMAIL" : settings.AUTHOR_EMAIL,
"AUTHOR_TITLE" : settings.AUTHOR_TITLE,
}
def md2html(request): #md2html工具页
return render(request, 'tools_html/md2html.html',
{
}
)
除了个别的网站还是用的函数以外基本网站的主体都换成了类视图。
他们在rul的配置上也是有区别的:
普通视图函数:
from .views import index
urlpatterns = [ url(r'^index/$', index, name='index'),]
通用类视图:
from .views import ClassListView
urlpatterns = [ url(r'^index/$', ClassListView.as_view(), name='index'),]
使用函数式图的时候要定义多个views可以使用 from xx import xx as x1
这样可以避免视图的重复
比如在model里可以给类写一个get_ordering函数然后根据前端给的参数不同在不同地来进行排序。
之前地界面中由于后端地功能性太少了,就导致前端里地计算也很繁琐。
反正当我开发评论功能地时候就乱成一锅粥了。
- 类视图相比函数视图的化重复性更高一些,使用函数式图的时候总感觉只能render一个界面,导致后面网站更加复杂后,业务逻辑关系就很复杂了。
最后,感谢tendcode的帮助和解答。。
本作品采用《CC 协议》,转载必须注明作者和本文链接