YOLO813

Django初学(5)- 视图快速建立翻页

    ListView,快速实现数据列表页。数据库里面填充了100条数据,示例代码如下:

from django.views.generic import ListView
class ListTest(ListView):
    # 引用的模型名称
    model = Article
    # 引用的模板
    template_name = 'Article-Test.html'
    # 一页多少条数据
    paginate_by = 10
    # 上下文名称,即模板中调用的变量
    context_object_name = 'my_articles'
    # 排序规则
    ordering = '-pk'
    # 翻页参数
    page_kwarg = 'p'

ListView类会调用get_context_data方法,所以如果我们还想传递上下文,可以在此方法中先获取父类的context,再增加自己想要的内容(这是因为如果不先调用父类,那么很多参数我们就用不了);get_queryset会默认使用all()返回所有的queryset,我们也可以进行修改,过滤掉自己不想要的数据,但是我在测试这个方法的时候,命令行提示Pagination may yield inconsistent results with an unordered object_list,我就很奇怪,明明上面定义了ordering,怎么会没有排序呢?

def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["user_name"] = 'fire'
        print("*"*30)
        for i,v in context.items():
            print(str(i) + ': '+str(v))
        print("*"*30)
        return context
def get_queryset(self):
  # return super().get_queryset()
  return Article.objects.filter(pk__lte=95)

我看了下源码,如下,确实是获取到了排序规则的

ordering = self.get_ordering() # -pk
if ordering:
  if isinstance(ordering, str): # yes
      ordering = (ordering,)
    queryset = queryset.order_by(*ordering)
return queryset

再仔细看了下自己写的代码,悟了,里面唯一的区别在于我没有继承django自身get_queryset的方法,换一种写法就可以了

return super().get_queryset().filter(pk__lte =10)

super().get_queryset()是一个queryset对象,或者不想继承django自身的方法,自己在Article.objects.filter(pk__lte=95)后面跟上排序的order规则也可以。

    在ListView的get_context_data方法中,context对象中的paginator和page_obj会经常使用,二者的导出路径均在django.core.paginator下,paginator对应的类是Paginator,常用方法num_pages,取得总页数,page_range取得页数范围值,count取得总条数,需要注意的是,该项针对的是你在get_queryset中所筛选出的数据,例如,当你设置了pk__lte =10,那么count只会取得pk__lte =10满足的数据条目!其中page_obj对应的是Page类,主要有has_next,has_previous,previous_page_number,number(当前页),next_page_number,start_index(获取当页第一条数据下标,即在总列表中第几条索引),end_index。

    如下利用Bootstrap和ListView快速实现普通分类页

    其中模板页面利用paginator和page_obj的一些属性,判断链接跳转,以及是否存在第一页/最后一页以禁止点击。源代码如下:

# Article-List.html
<ul>
{% for article in my_arts %}
<li>{{article.title}}</li>
{% endfor %}
</ul>
<ul class="pagination">
{# 上一页 #}
{% if not page_obj.has_previous %}
<li class='disabled'><a href="javascript:void(0)" >上一页</a></li>
{% else %}
# 两种写法任选其一
{% comment %} <li><a href="?p={{page_obj.previous_page_number}}" >上一页</a></li> {% endcomment %}
<li><a href="{% url 'front:index' %}?p={{page_obj.previous_page_number}}">上一页</a></li>
{% endif %}
{# 中间页 #}
{% for index in paginator.page_range %}
{% if index == page_obj.number %}
<li class='active'>
{% else %}
<li>
{% endif %}
<a href="?p={{index}}">{{index}}</a></li>
{% endfor %}
{# 下一页 #}
{% if not page_obj.has_next %}
<li class='disabled'><a href="javascript:void(0)" >下一页</a></li>
{% else %}
<li><a href="?p={{page_obj.next_page_number}}" >下一页</a></li>
{% endif %}
</ul>

但是这个翻页有一个问题,就是展示了所有的分页,一旦数据有成千上万条,那翻页的摆放就懵逼了,所以我们可以再更新一下翻页的代码,如下:


可以看到,前端页面仅显示了当前选中页面的左右两边各三页的链接,前后各增加第一页和最后一页的链接,中间全部用省略号代替,前端html页面和后端视图代码如下:

# articles.html
{# 上一页 #}
{% if not page_obj.has_previous %}
    <li class='disabled'><a href="javascript:void(0)">上一页</a></li>
{% else %}
    <li><a href="{% url 'front:index' %}?p={{page_obj.previous_page_number}}">上一页</a></li>
{% endif %}
{# 中间的页数处理#}
{% if left_has_more %}
    <li><a href="{% url 'front:index' %}?p=1">1</a></li>
    <li><a href="javascript:void(0)">...</a></li>
{% endif %}
{% for left_page in left_pages %}
    <li><a href="{% url 'front:index' %}?p={{left_page}}">{{left_page}}</a></li>
{% endfor %}
    {# 当前页#}
<li class='active'><a href="javascript:void(0)">{{current_page}}</a></li>
{% for right_page in right_pages %}
    <li><a href="{% url 'front:index' %}?p={{right_page}}">{{right_page}}</a></li>
{% endfor %}
{% if right_has_more %}
    <li><a href="javascript:void(0)">...</a></li>
    <li><a href="{% url 'front:index' %}?p={{num_pages}}">{{num_pages}}</a></li>
{% endif %}
{# 下一页 #}
{% if not page_obj.has_next %}
    <li class='disabled'><a href="javascript:void(0)">下一页</a></li>
{% else %}
    <li><a href="{% url 'front:index' %}?p={{page_obj.next_page_number}}">下一页</a></li>
{% endif %}


视图层:

class ListClass(ListView):
    template_name = 'articles.html'
    model= Article
    page_kwarg = 'p'
    paginate_by = 10
    ordering = 'pk'
    context_object_name = "my_articles"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        res = self.getPaginationInfo(context['paginator'], context['page_obj'])
        context.update(res)
        return context

    def getPaginationInfo(self, paginator, page_obj, around_page=3):
        current_page = page_obj.number
        left_has_more = False
        right_has_more = False
        if current_page - around_page > 1:
            left_pages = range(current_page - around_page, current_page)
            left_has_more = True
        else:
            left_pages = range(1, current_page)
        if current_page + 1 + around_page > paginator.num_pages:
            right_pages = range(current_page + 1, paginator.num_pages + 1)
        else:
            right_pages = range(current_page + 1, current_page + around_page + 1)
            right_has_more = True
        return {
            'left_pages': left_pages,
            'right_pages': right_pages,
            'current_page':current_page,
            'left_has_more':left_has_more,
            'right_has_more':right_has_more,
            'num_pages':paginator.num_pages
        }

实现原理也很简单,就是利用paginator和page_obj的一些属性,函数,通过getPaginationInfo新增的around_page参数进行比较,只要当前页比around_page大1,那么说明左边肯定至少有around_page+1条数据,右边的处理也是同理。