Django 教程之View视图

所谓的视图,就是MVC或MTV中的视图,负责业务逻辑,返回对应的响应,并在适当时候调用Model和Template。 官方文档

先举个例子

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。**

    注意,视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够比较准确地反映出它实现的功能。

  • 这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都负责返回一个HttpResponse对象。

当 Django 接受一个请求之后(严格来说是 HTTP 请求,只不过 HTTP 请求会被 Django 转化
为 request对象),请求会先经过所有 middleware 的 process_request 方法,然后解析 URL,
接着根据配置的 URL 和 View 的映射,把 request 对象传递到 View 中 。

FBV和CBV

  • FBV (function base views)
  • CBV (class base views)

​ 单纯从技术上来说, function view (函数视图)和 class-based view 并没有高低之分,有的仅
仅是对场景的适用性。

​ 首先需要对比的一个概念是函数和类 。
​ 什么情况下需要使用函数,什么情况下需要封装出一个类呢?简单来说,只要代码的逻辑
被重复使用, 同时有需要共享的数据,就可以考虑封装出一个类 。 这样就可以享用类提供的好处
了一一继承和复用 。 类的通用视图的可扩展性和灵活性远超基于函数的通用视图。
​ 而如果这种情况下依然使用函数的话,就需要定义多个子函数,通过函数级别的复用来达到
目的 。 但问题在于不够结构化,无法通过继承一个结构(类),然后修改其中某个配置,或者重
写某个方法达到复用的目的 。

function view 的处理逻辑比较好理解,就是简单的函数,流程就是函数的执行流程,只是第
一个参数是 request 对象。

class-based view 对外暴露的接口其实是 as_view,as_view 其实只做了一件事,那就是返回一个闭包。这个闭包会在同ango 解析完请求之后调用,而闭包中的逻辑是这样的:

  • 给 class (也就是我们定义的 View 类)赋值一一request 、 args 和 kwargs 。
  • 根据 HTTP 方法分发请求 。 比如 HTTP GET 请求会调用 class.get 方法, POST 请求会
    调用 class.post 方法 。

FBV (function base views)

FBV(function base views) 就是在视图里使用函数处理请求。

def index(request):
    return HttpResponse("欢迎访问我的博客首页!")

上面只是简单实现了从 URL 到 View 的数据映射 ,接着来填充简单的内容 。

from django.shortcuts import render, HttpRespons

def index(request):
    return render(request, 'index.html', context={'name': 'hello'})

这里需要稍稍介绍一下 render 方法,它接收的参数如下:

render(request, template_name, context=None, content_type=None, status=None,
using=None)

其中各参数的意义如下 。

  • request : 封装了 HTTP 请求的 request 对象。
  • template_name : 模板名称,可以像前面的代码那样带上路径 。
  • context : 字典数据,它会传递到模板中 。
  • cntent_type : 页面编码类型,默认值是 text/html 。
  • status :状态码,默认值是 200
  • using :使用|哪种模板引擎解析,这可以在settings 中配置,默认使用 Django 自带的模板。

CBV (class base views)

CBV(class base views) 就是在视图里使用类处理请求。

Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以 Django 在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:

  • 提高了代码的复用性,可以使用面向对象的技术,比如 Mixin
  • 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多 if 判断,提高代码可读性

Django 文档中 class-based view 的具体解释:

​ View 就是一个能够接受请求并且返回响应的可调用对象,它不仅仅是一 个函数 。
同时 Django 提供了一些类作为 View 的示例 。 这样就九许我们结构化 View 并且通过继
承和混入( mixin )的方式来复用代码 。

mixin是多重继承的一种,它将父类的行为和属性结合在一起

使用

注意:使用CBV时,urls.py 中也做对应的修改:

urlpatterns = [
    url(r'^$', IndexView.as_view(), name='index'),  
]

使用Class-based View有两种方法

  • 一个是在as_view()中直接传入参数,它会覆盖掉该View原有的属性,这种情况只适用于处理不复杂的情况:
from django.conf.urls import patterns
from django.views.generic import TemplateView
 
urlpatterns = patterns('',
    (r'^about/', TemplateView.as_view(template_name="about.html")),
)
  • 另外一个就是直接继承该View,然后覆盖其中的方法,属性等,实现自己想要的功能,在URLconf中,直接使用as_view()就可以了,不用传递参数:
from django.views.generic import TemplateView
 
class AboutView(TemplateView):
    template_name = "about.html"
from django.conf.urls import patterns
from some_app.views import AboutView
 
urlpatterns = patterns('',
    (r'^about/', AboutView.as_view()),
)

View分享

from django.shortcuts import render

# Create your views here.
from django.views.generic import ListView, DetailView
from .models import Article, Category, Tag
from haystack.generic_views import SearchView
from django.conf import settings
from haystack.query import SearchQuerySet
import markdown
from django.shortcuts import get_object_or_404
from django.utils.text import slugify
from markdown.extensions.toc import TocExtension
import re

class IndexView(ListView):
    model = Article
    template_name = 'index.html'
    context_object_name = 'article_index'
    paginate_by = 5

    def get_context_data(self, **kwargs):
        """
        在视图函数中将模板变量传递给模板是通过给 render 函数传递一个字典实现的
        例如 render(request, 'blog/index.html', context={'post_list': post_list})
        这里传递了一个 {'post_list': post_list} 字典给模板。
        在类视图中,这个需要传递的模板变量字典是通过 get_context_data 获得的,
        所以我们复写该方法,以便我们能够自己再插入一些我们自定义的模板变量进去。
        """

        # 首先获得父类生成的传递给模板的字典
        context = super().get_context_data(**kwargs)

        # 父类生成的字典中已有 paginator、page_obj、is_paginated 这三个模板变量
        # paginator 是 Paginator 的一个实例
        # page_obj 是 Page 的一个实例
        # is_paginated 是一个布尔变量,用于指示是否已分页。
        # 例如如果规定每页 10 个数据,而本身只有 5 个数据,其实就用不着分页,此时 is_paginated=False。
        # 关于什么是 Paginator,Page 类在 使用 Django Pagination 实现简单的分页功能:http://zmrenwu.com/post/23/
        # 中已有详细说明。
        paginator = context.get('paginator')
        page = context.get('page_obj')
        is_paginated = context.get('is_paginated')

        # 调用自己写的 pagination_data 方法获得显示分页导航条需要的数据
        pagination_data = self.pagination_data(paginator, page, is_paginated)

        # 将分页导航条的模板变量更新到 context 中
        context.update(pagination_data)

        # 将更新后的 context 返回,以便 ListView 使用这个字典中的模板变量去渲染模板
        # 记住此时字典中已有了显示分页导航条所需的数据
        return context

    def pagination_data(self, paginator, page, is_paginated):
        if not is_paginated:
            # 如果没有分页,则无需显示分页导航条,不用任何分页导航条的数据,因此返回一个空的字典
            return {}

        # 当前页左边连续的页码号,初始值为空
        left = []

        # 当前页右边连续的页码号,初始值为空
        right = []

        # 标示第一页页码后是否需要显示省略号
        left_has_more = False

        # 标示最后一页页码前是否需要显示省略号
        right_has_more = False

        # 标示是否需要显示第一页的页码号。
        # 因为如果当前页左边的连续页码号中已经含有第一页的页码号,此时就无需再显示第一页的页码号
        # 其它情况下第一页的页码是始终需要显示的。
        first = False

        # 标示是否需要显示最后一页的页码号。
        # 需要此指示变量的理由和上面相同。
        last = False

        # 获得用户当前请求的页码号
        page_number = page.number

        # 获得分页后的总页数
        total_pages = paginator.num_pages

        # 获得整个分页页码列表,比如分了四页,那么就是 [1, 2, 3, 4]
        page_range = paginator.page_range

        if page_number == 1:
            # 如果用户请求的是第一页的数据,那么当前页左边的不需要数据,因此 left=[](已默认为空)
            # 获取当前页右边的连续页码号。
            # 比如分页页码列表是 [1, 2, 3, 4],那么获取的就是 right = [2, 3]
            # 这里只获取了当前页码后连续两个页码,你可以更改这个数字以获取更多页码。
            right = page_range[page_number:page_number + 2]

            # 如果最右边的页码号比最后一页的页码号减去 1 还要小,
            # 说明最右边的页码号和最后一页的页码号之间还有其它页码,因此需要显示省略号,通过 right_has_more 来指示
            if right[-1] < total_pages - 1:
                right_has_more = True

            # 如果最右边的页码号比最后一页的页码号小,说明当前页右边的连续页码号中不包含最后一页的页码
            # 所以需要显示最后一页的页码号,通过 last 来指示
            if right[-1] < total_pages:
                last = True

        elif page_number == total_pages:
            # 如果用户请求的是最后一页的数据,那么当前页右边就不需要数据,因此 right=[](已默认为空)
            # 获取当前页左边的连续页码号。
            # 比如分页页码列表是 [1, 2, 3, 4],那么获取的就是 left = [2, 3]
            # 这里只获取了当前页码后连续两个页码,你可以更改这个数字以获取更多页码。
            left = page_range[(page_number - 3)
                              if (page_number - 3) > 0 else 0:page_number - 1]

            # 如果最左边的页码号比第 2 页页码号还大,
            # 说明最左边的页码号和第一页的页码号之间还有其它页码,因此需要显示省略号,通过 left_has_more 来指示
            if left[0] > 2:
                left_has_more = True

            # 如果最左边的页码号比第一页的页码号大,说明当前页左边的连续页码号中不包含第一页的页码
            # 所以需要显示第一页的页码号,通过 first 来指示
            if left[0] > 1:
                first = True
        else:
            # 用户请求的既不是最后一页,也不是第一页,则需要获取当前页左右两边的连续页码号
            # 这里只获取了当前页码前后连续两个页码,你可以更改这个数字以获取更多页码。
            left = page_range[(page_number - 3)
                              if (page_number - 3) > 0 else 0:page_number - 1]
            right = page_range[page_number:page_number + 2]

            # 是否需要显示最后一页和最后一页前的省略号
            if right[-1] < total_pages - 1:
                right_has_more = True
            if right[-1] < total_pages:
                last = True

            # 是否需要显示第一页和第一页后的省略号
            if left[0] > 2:
                left_has_more = True
            if left[0] > 1:
                first = True

        context = {
            'left': left,
            'right': right,
            'left_has_more': left_has_more,
            'right_has_more': right_has_more,
            'first': first,
            'last': last,
        }

        return context


class CategoryView(IndexView):
    def get_queryset(self):
        category = get_object_or_404(Category, name=self.kwargs.get('name'))
        return super().get_queryset().filter(category=category)
        # return self.model.objects.filter(category=category)


class TagView(IndexView):
    def get_queryset(self):
        tag = get_object_or_404(Tag, pk=self.kwargs.get('pk'))
        return super().get_queryset().filter(tags=tag)


class ArticleView(DetailView):
    model = Article
    template_name = 'article.html'
    context_object_name = 'article_post'

    def get_object(self, queryset=None):
        obj = super().get_object(queryset=None)
        # 文章可以使用markdown书写,带格式的文章,像csdn写markdown文章一样
        md = markdown.Markdown(extensions=[
            'markdown.extensions.extra',
            'markdown.extensions.codehilite',
            TocExtension(slugify=slugify),
        ])
        obj.body = md.convert(obj.body)
        # m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
        # obj.toc = m.group(1) if m is not None else ''
        #{% if obj.toc %}
        # https://juejin.im/post/5d5ddec4f265da038f4812ed
        obj.toc = md.toc
        return obj

# 重写搜索视图,可以增加一些额外的参数,且可以重新定义名称


class MySearchView(SearchView):
    # 返回搜索结果集
    context_object_name = 'search_list'

    # 搜索结果以浏览量排序
    queryset = SearchQuerySet()


class ArchivesView(ListView):
    model = Article
    template_name = 'archives.html'
    context_object_name = 'article_archives'

    def get_queryset(self):
        sql_queryset_year = Article.objects.raw(
            'select id,title,DATE_FORMAT(create_date,"%%Y") as create_date_year from blog_article order by create_date_year DESC')
        return sql_queryset_year


def AboutView(request):
    return render(request, 'about.html')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值