Django来敲门~第一部分【9.使用Django内置视图处理对象简化开发】

常使民无知、无欲,使夫智者不敢为也。为无为,则无不治
——老子《道德经》


首先,要说这是Django基础部分的最后一个章节了。后面会详细讲解Django每一部分的使用方式和API了

本节内容

  • 内置视图处理对象的定义
  • 内置视图处理对象的使用
  • 源代码分析

1. 内置视图处理对象的定义

对于我们视图处理函数,从最原始的HttpResponse()来进行页面数据的输出,到render()函数进行模板页面的操作,已经是可以满足我们的需求了,但是Django赶脚还不够,不够~所以提供了常规的一些页面视图对象对页面模板和模板中要展示的数据进行了封装处理。

常规情况下,对应页面中要展示数据列表的视图,通过继承Django的django.views.generic.ListView对象来实现,页面中要展示数据信息的视图,通过继承Django的django.views.generic.DetailView进行实现。

我们改造polls/views.py视图模块如下:

from django.shortcuts import render, get_object_or_404

from django.http import HttpResponseRedirect
from django.views import generic
from django.urls import reverse

from .models import Question


# 封装:定义展示应用首页的处理类
class IndexView(generic.ListView):
    # 模板路径
    template_name = "index.html"
    # 指定变量名称,保存查询到的数据
    context_object_name = "question_list"

    # 列表页面,需要查询对应的数据集合
    def get_queryset(self):
        return Question.objects.order_by("-pub_date")


# 封装:定义展示问题详细信息的处理类
class DetailView(generic.DetailView):
    # 指定要展示的数据类型
    model = Question
    # 指定展示的模板页面
    template_name = "details.html"


# 封装:定义展示结果的视图处理类
class ResultView(generic.DetailView):
    model = Question
    template_name = "results.html"


# 定义投票结果
def vote(request, question_id):
    # 获取查询的问题对象
    question = get_object_or_404(Question, pk=question_id)
    print(request.POST)
    try:
        select_choice = question.choice_set.get(pk=request.POST['choice'])
    except Exception as e:
        print(e)
        return render(request, "details.html",
                      {
                          "question": question,
                          "error_message": "你的问题还没有发布解决方案"
                      })
    else:
        # 投票数量增加1
        select_choice.votes += 1
        # 保存到数据库
        select_choice.save()
        # 代码中使用配置的方式跳转到指定的路由
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))

上述代码中,对于展示问题列表的首页,继承了generic.ListView内置类,通过template_name指定了HTML视图,通过context_object_name指定了保存数据的变量,在get_queryset()函数中定义了数据的查询方式

问题详细信息页面和问题解决方案的结果展示页面,继承了generic.DetailView内置类,通过model指定了展示的数据的类型,template_name指定了HTML视图的名称

vote函数暂时不做任何处理,这是一个包含了业务逻辑处理的视图函数

2. 视图对象的使用

经过上述的改造,我们可以看到视图处理函数已经变得非常的简洁,但是存在这样一个问题,IndexView处理类中的数据,我们可以看懂是通过get_queryset()函数进行查询的,然后自动复制给变量question_list,那么后面的DetailViewResultView中的数据是怎么来的呢?

DetailViewResultView中的要查询的数据,同样也是通过主键编号进行查询的,默认的变量名称是pk,所以在路由中要进行如下的修改配置,才可以正常使用

改造polls/urls.py路由模块

from django.conf.urls import url
from . import views

app_name = "polls"

urlpatterns = [
    # url(r"^$", views.index, name="index"),
    # url(r"^(?P<question_id>\d+)$", views.detail, name="detail"),
    # url(r"^(?P<question_id>\d+)/results/$", views.results, name="results"),
    url(r"^$", views.IndexView.as_view(), name="index"),
    url(r"^(?P<pk>\d+)/$", views.DetailView.as_view(), name="detail"),
    url(r"^(?P<pk>\d+)/results/$", views.ResultView.as_view(), name="results"),
    url(r"^(?P<question_id>\d+)/vote/$", views.vote, name="vote"),
]

这里路由配置的过程中,通过as_view()函数,会自动对目标视图类进行封装,将视图处理类中的template_name模板进行加载,然后渲染由model或者context_object_name指定的数据。

2.1. 项目测试

重启项目,打开网页进行访问

访问首页
查看问题
投票操作

3. 源码分析

此时,会有个问题困扰着我们,为什么要继承视图处理类,为什么要继承不同的处理类ListViewDetailView

这个问题就对了,我们看看Django源代码

首先查看ListView

class ListView(MultipleObjectTemplateResponseMixin, BaseListView):
    """
    Render some list of objects, set by `self.model` or `self.queryset`.
    `self.queryset` can actually be any iterable of items, not just a queryset.
    """
......
class MultipleObjectMixin(ContextMixin):
    """
    A mixin for views manipulating multiple objects.
    """
    allow_empty = True 
    queryset = None
    model = None
    paginate_by = None
    paginate_orphans = 0
    context_object_name = None
    paginator_class = Paginator
    page_kwarg = 'page'
    ordering = None
......
    def get_queryset(self):
        """
        Return the list of items for this view.

        The return value must be an iterable and may be an instance of
        `QuerySet` in which case `QuerySet` specific behavior will be enabled.
        """
......

我们可以从上述代码中看到,继承自ListView视图处理类的类型,已经在内部封装好了分页操作功能,这也是我们项目中经常用到的技术呢,非常棒吧,我们继承了这个视图处理对象,就可以在后续的项目中,通过重写它的属性和方法来快捷的完成数据展示的分页功能了。

接下来,我们继续看DetailView是怎么操作的吧

class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
    """
    Render a "detail" view of an object.

    By default this is a model instance looked up from `self.queryset`, but the
    view will support display of *any* object by overriding `self.get_object()`.
    """
......
class SingleObjectMixin(ContextMixin):
    """
    Provides the ability to retrieve a single object for further manipulation.
    """
    model = None
    queryset = None
    slug_field = 'slug'
    context_object_name = None
    slug_url_kwarg = 'slug'
    pk_url_kwarg = 'pk'
    query_pk_and_slug = False  
......

我们可以在源代码中,看到各种操作的一些信息,官方注释用于使用对象数据来渲染详细信息页面的视图对象,继承的父类中,也定义了各种属性字段用于控制单个对象数据的展示操作

以上两个视图类,都间接继承了View内置类,我们看一下源代码

# 基础视图对象工具类,用于进行视图的渲染操作,包含了as_view()处理函数
class View(object):
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """
    ......
    @classonlymethod
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        for key in initkwargs:
        ......
# 用于进行视图模板和数据混合渲染的工具类,可以看到包含了template_name字段
class TemplateResponseMixin(object):
    """
    A mixin that can be used to render a template.
    """
    template_name = None
    template_engine = None
    response_class = TemplateResponse
    content_type = None
......
# 用于封装基础模板渲染页面视图的工具类
class TemplateView(TemplateResponseMixin, ContextMixin, View):
    """
    A view that renders a template.  This view will also pass into the context
    any keyword arguments passed by the URLconf.
    """
......
# 用于请求最终重定向的视图工具类
class RedirectView(View):
    """
    A view that provides a redirect on any GET request.
    """
......

从基础文件view.py中,我们可以看到,各种视图模板的基础处理操作和视图类型的封装都有了简洁的定义,我们需要做的就是在项目中,继承这些Django已经封装好的对象,快捷的完成项目的开发。


Django来敲门
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容