django 入门笔记:通用视图类重构视图

django 及 rest_framework 笔记链接如下:
django 入门笔记:环境及项目搭建
django 入门笔记:数据模型
django 入门笔记:视图及模版
django 入门笔记:Admin 管理系统及表单
django 入门笔记:通用视图类重构视图
django_rest_framework 入门笔记:Serializer
django_rest_framework 入门笔记:视图函数重构
django_rest_framework 入门笔记:分页,多条件筛选及权限认证设置
django 自带 user 字段扩展及头像上传

终于到最后一部分了,这部分我们将通过 django 自带的通用视图类替换之前写的视图函数,对视图进行重构

利用 django 通用视图类创建类视图
  1. 创建视图类

    import markdown
    from django.shortcuts import render
    from django.views.generic import ListView, DetailView
    from django.shortcuts import get_object_or_404
    from blog.models import Post
    
    # 获取相应模型下的全部数据
    def home(request):
        post_list = Post.objects.all()
        return render(request, 'blog/home.html', locals())
    
    # 通过 ListView 类来进行修改
    class HomeView(ListView):
        model = Post # 指定视图模型
        template_name = 'blog/home.html' # 指定渲染的模版
        context_objects_name = 'post_list' # 对应的模型列表数据保存的变量名
    
    # #################################################################################
    # 获取特定条件下的模型数据 
    def category(request, pk):
        category = get_object_or_404(Category, pk=pk)
        post_list = Post.objects.filter(category=category)
        return render(request, 'blog/home.html', locals())
    
    # 通过 ListView 类进行修改
    # 基本属性同 HomeView 相同,也可以直接继承 HomeView 然后复写 get_queryset() 方法实现
    class CategoryView(ListView):
        model = Post
        template_name = 'blog/home.html'
        context_objects_name = 'post_list'
        
        # 该方法默认返回指定模型的全部数据,通过复写该方法,改变默认行为
        def get_queryset(self):
            # 类视图中,从 url 捕获的命名组参数值保存在实例的 kwargs 中,是一个字典
            # 非命名组参数值保存在实例的 args 中,是一个列表
            category = get_object_or_404(Category, pk=kwargs.get('pk'))
            return super(CategoryView, self).get_queryset().filter(category=category)
    
    # #################################################################################
    # 获取具体的详情
    def post_detail(request, pk):
        post = get_object_or_404(Post, pk=pk)
        post.increase_views()
        post.body = markdown.markdown(post.body, extensions=[
            'markdown.extensions.extra',
            'markdown.extensions.codehilite',
        ])
        form = CommentForm()
        return render(request, 'blog/detail.html', locals())
    
    class PostDetailView(DetailView):
        model = Post
        template_name = 'blog/detail.html'
        context_objects_name = 'post'
        
        # 方法返回一个 HttpResponse 实例
        def get(self, request, *args, **kwargs):
            # get 方法会通过调用 get_object 和 get_context——data 方法对模版渲染
            # def get(self, request, *args, **kwargs):
             # self.object = self.get_object()
             # context = self.get_context_data(object=self.object)
             # return self.render_to_response(context)
            response = super(PostDetailView, self).get(request, *args, **kwargs)
            # 只有当 get 方法被调用后才有 self.object 属性,即 post 实例
            # 对应 post_detail 函数中的 post.increase_views()
            self.object.increase_views()
            return response
        
        # 根据 post 的 pk 值获取相应的 post 实例
        def get_object(self, queryset=None):
            post = super(PostDetailView, self).get_object(queryset=None)
            post.body = markdown.markdown(post.body, extensions=[
             'markdown.extensions.extra',
             'markdown.extensions.codehilite',
         ])
            return post
        
        # 返回一个字典,为模版变量字典,传递给相应的模版
        def get_context(self, **kwargs):
            context = super(PostDetailView, self).get_context(**kwargs)
            form = CommentForm()
            # 更新 context 的内容,必须调用
            context.update(locals())
            return context
    
  2. 绑定 url

    urlpatterns = [
     # url(r'^home/$', views.home, name='home'),
     url(r'^home/$', views.HomeView.as_view(), name='home'),
     # url(r'cate/(?P<pk>[0-9]+)/$', views.category, name='cate'),
     url(r'cate/(?P<pk>[0-9]+)/$', views.CategoryView.as_view(), name='cate'),
     # url(r'post/(?P<pk>[0-9]+)/$', views.post_detail, name='post'),
     url(r'post/(?P<pk>[0-9]+)/$', views.PostDetailView.as_view(), name='post'),
    ]
    

修改完后的界面应该和之前的效果是一样的


列表界面.png
详情界面

有时候如果我们的数据过多,同一页加载全部数据,用户的体验肯定不好,我们通过通用视图类来创建分页,这边为了方便显示,我们会设置每页加载一篇文章

通过 ListView 创建分页
  1. 指定 ListView 中的 paginate_by 属性来设置分页

    class PostListView(ListView):
        model = Post
        template_name = 'blog/home.html'
        context_objects_name = 'post_list'
        # 指定分页,每页数量为 1
        paginate_by = 1
    
  2. 在模版中加入分页

    {# ...... #}
    {% if is_paginated %}
     <div class="pagination-simple">
         {% if page_obj.has_previous %}
             <a href="?page={{ page_obj.previous_page_number }}">Previous</a>
         {% endif %}
             <span class="current">
                     Page {{ post_list.number }} of {{ post_list.paginator.num_pages }}
             </span>
            {% if page_obj.has_next %}
             <a href="?page={{ page_obj.next_page_number }}">Next</a>
            {% endif %}
     </div>
    {# ...... #}
    
通过 Paginator 创建分页
  1. 创建相应的视图

    def home(request):
        limit = 10
        posts = Post.object.all()
        paginator = Paginator(posts, limit)
        
        # 根据表单获取页码
        page = request.GET.get('page')
        try:
            post_list = paginator.page(page) # 获取 num 页码下的列表
        except PageNotAnInteger:
            post_list = paginator.page(1) # 如果 page 不是整数则返回第一页列表
     except EmptyPage:
            post_list = paginator.page(paginator.num_pages) # 如果没有数据则返回最后一页列表
     
        return render(request, 'blog/home.html', locals())
    
  2. 通过模版进行渲染

    {% for post in post_list %}
        {{ post.title }}<br />
        ...
    {% endfor %}
    
    <div class="pagination">
        <span class="step-links">
            {% if post_list.has_previous %}
                <a href="?page={{ post_list.previous_page_number }}">previous</a>
            {% endif %}
            
            <span class="current">
                Page {{ post_list.number }} of {{ post_list.paginator.num_pages }}
            </span>
    
            {% if post_list.has_next %}
                <a href="?page={{ post_list.next_page_number }}">next</a>
            {% endif %}
        </span>
    </div>
    

最后做了分页的效果界面
带分页列表

目前的分页效果看上去并不那么美观,在实际项目中,我们优化了分页的显示,具体的代码就不贴了(我怕代码太多你们会打我),可以下载项目查看,这边我们可以看下效果图
优化分页列表

最后我们列下 Paginator 常用属性结束 django 的入门教程,接下来会有 DRF 的入门教程,django 算是其基础吧,DRF 实现了前后端分离,刚好适合我这种 Android 开发需要接口的。

from django.core.paginator import Paginator
item_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n']
# 指定 paginator 的列表以及每页显示的列表数量
p = Paginator(item_list, 2)
print(p.count) # 返回列表的总数    14
print(p.num_pages) # 返回总页数    7
print(p.page_range) # 返回页数的范围   (1, 8)
print(p.per_page) # 返回每页列表的数量
print(p.object_list) # 返回所有的列表 item
# 通过 page(num) 方法获取 num 页的列表 <Page 2 of 7>
page2 = p.page(2)
print(page2.number) # 获取当前页的页码
print(page2.object_list) # 获取该页码下的所有列表    ['c', 'd']
print(page2.has_next()) # 是否有下页    True
print(page2.has_previous()) # 是否有上页    True
print(page2.has_other_pages()) # 是否有其他页    True
# 如果没有上/下一页则返回 EmptyPage 错误 EmptyPage: That page contains no results
print(page2.next_page_number()) # 获取下一页的页码    3
print(page2.previous_page_number()) # 获取上一页的页码    1
print(page2.start_index()) # 当前页第一个 item 在列表中的位置    3
print(page2.end_index()) # 当前页最后一个 item 在列表中的位置    4
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容