django 入門筆記:通用視圖類重構視圖

django 及 rest_framework 筆記鏈接如下:
django 入門筆記:環(huán)境及項目搭建
django 入門筆記:數據模型
django 入門筆記:視圖及模版
django 入門筆記:Admin 管理系統(tǒng)及表單
django 入門筆記:通用視圖類重構視圖
django_rest_framework 入門筆記:Serializer
django_rest_framework 入門筆記:視圖函數重構
django_rest_framework 入門筆記:分頁,多條件篩選及權限認證設置
django 自帶 user 字段擴展及頭像上傳

終于到最后一部分了,這部分我們將通過 django 自帶的通用視圖類替換之前寫的視圖函數,對視圖進行重構

利用 django 通用視圖類創(chuàng)建類視圖
  1. 創(chuàng)建視圖類

    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
詳情界面

有時候如果我們的數據過多,同一頁加載全部數據,用戶的體驗肯定不好,我們通過通用視圖類來創(chuàng)建分頁,這邊為了方便顯示,我們會設置每頁加載一篇文章

通過 ListView 創(chuàng)建分頁
  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 創(chuàng)建分頁
  1. 創(chuàng)建相應的視圖

    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>
    

最后做了分頁的效果界面
帶分頁列表

目前的分頁效果看上去并不那么美觀,在實際項目中,我們優(yōu)化了分頁的顯示,具體的代碼就不貼了(我怕代碼太多你們會打我),可以下載項目查看,這邊我們可以看下效果圖
優(yōu)化分頁列表

最后我們列下 Paginator 常用屬性結束 django 的入門教程,接下來會有 DRF 的入門教程,django 算是其基礎吧,DRF 實現了前后端分離,剛好適合我這種 Android 開發(fā)需要接口的。

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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容