更换网站函数视图为类视图

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的帮助和解答。。

原创文章,转载请注明出处:https://boywithacoin.cn/article/djangode-lei-shi-tu-he-han-shu-shi-tu/