13 分页功能

1. 为什么要分页功能

  1. 新增或编辑博客内容
    1. 博文数目较多 -> 全部加载过慢 -> 分页加载缓冲
    2. 夯实基础: 熟悉shell模型、模型操作、模板标签、分页器

2. 快速添加博文

shell命令行添加博文

python manage.py shell for 循环执行新增博文代码 exit()退出

3. 模型新增对象

from s2aclab.models import Articles article = Articles() article.title = ‘xxx’ … artclie.save()

dir() 查看变量的方法 dir(article) [‘DoesNotExist’, ‘MultipleObjectsReturned’, ‘class‘, ‘delattr‘, ‘dict‘, ‘dir‘, ‘doc‘, ‘eq‘, ‘format‘, ‘ge‘, ‘getattribute‘, ‘getstate‘, ‘gt‘, ‘hash‘, ‘init‘, ‘init_subclass‘, ‘le‘, ‘lt‘, ‘module‘, ‘ne‘, ‘new‘, ‘reduce‘, ‘reduce_ex‘, ‘repr‘, ‘setattr‘, ‘setstate‘, ‘sizeof‘, ‘str‘, ‘subclasshook‘, ‘weakref‘, ‘_check_column_name_clashes’, ‘_check_field_name_clashes’, ‘_check_fields’, ‘_check_id_field’, ‘_check_index_together’, ‘_check_local_fields’, ‘_check_long_column_names’, ‘_check_m2m_through_same_relationship’, ‘_check_managers’, ‘_check_model’, ‘_check_model_name_db_lookup_clashes’, ‘_check_ordering’, ‘_check_swappable’, ‘_check_unique_together’, ‘_do_insert’, ‘_do_update’, ‘_get_FIELD_display’, ‘_get_next_or_previous_by_FIELD’, ‘_get_next_or_previous_in_order’, ‘_get_pk_val’, ‘_get_unique_checks’, ‘_meta’, ‘_perform_date_checks’, ‘_perform_unique_checks’, ‘_save_parents’, ‘_save_table’, ‘_set_pk_val’, ‘_state’, ‘article_type’, ‘article_type_id’, ‘author’, ‘author_id’, ‘check’, ‘clean’, ‘clean_fields’, ‘content’, ‘created_time’, ‘date_error_message’, ‘delete’, ‘from_db’, ‘full_clean’, ‘get_deferred_fields’, ‘get_next_by_created_time’, ‘get_next_by_last_updated_time’, ‘get_previous_by_created_time’, ‘get_previous_by_last_updated_time’, ‘id’, ‘last_updated_time’, ‘objects’, ‘pk’, ‘prepare_database_save’, ‘refresh_from_db’, ‘save’, ‘save_base’, ‘serializable_value’, ‘title’, ‘unique_error_message’, ‘validate_unique’] article.last_updated_time datetime.datetime(2020, 6, 3, 7, 31, 7, 411026, tzinfo=)

  1. >>> from s2aclab.models import Articles,ArticleType
  2. >>> from django.contrib.auth.models import User
  3. >>> a = Articles()
  4. >>> a_type = ArticleType.objects.all()[0]
  5. >>> user = User.objects.all()[0]
  6. >>> for i in range(1,30):
  7. ... a = Articles()
  8. ... a.title = "for %s" % i
  9. ... a.content = "xxxx: %s" % i
  10. ... a.article_type = a_type
  11. ... a.author = user
  12. ... a.save()
  13. ...
  14. >>> Articles.objects.all().count()
  15. 32
  16. >>>

4. 分页器实现分页 Paginator

分页器: from django.core.paginator import Paginator 实例化: 具体如何分页 paginator = Paginator(object_list, each_page_count) 具体页面 page1 = paginator.page(1)

>>> from django.core.paginator import Paginator
>>> from s2aclab.models import Articles
>>> dir()
['Articles', 'Paginator', '__builtins__']
>>> arts = Articles.objects.all()
>>> arts.count()
32
>>> paginator = Paginator(arts,10)
<string>:1: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 's2aclab.models.Articles'> QuerySet.
>>>

上面警告是因为没有设置默认的排序方式

在s2aclab.models.Articles类中新建类

    class Meta: # 排序
        ordering = ['-created_time']

然后更新同步迁移数据库,重新开启本地服务。再重新分页

>>> from django.core.paginator import Paginator
>>> from s2aclab.models import Articles
>>> arts = Articles.objects.all()
>>> arts.count()
32
>>> paginator = Paginator(arts,10)
>>> paginator
<django.core.paginator.Paginator object at 0x000001C8185B4400>
>>> dir(paginator)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_object_list_is_ordered', '_get_page', 'allow_empty_first_page', 'count', 'get_page', 'num_pages', 'object_list', 'orphans', 'page', 'page_range', 'per_page', 'validate_number']
>>> paginator.count
32
>>> paginator.num_pages
4
>>> paginator.page_range
range(1, 5)
>>> page1 = paginator.page(1)
>>> page1
<Page 1 of 4>
>>> dir(page1)
['__abstractmethods__', '__class__', '__contains__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', 'count', 'end_index', 'has_next', 'has_other_pages', 'has_previous', 'index', 'next_page_number', 'number', 'object_list', 'paginator', 'previous_page_number', 'start_index']

http://localhost:8000/article/?page=2 GET请求访问页码

修改views.py

from django.core.paginator import Paginator
def articles_list(request):
    articles_all_list = Articles.objects.all()
    paginator = Paginator(articles_all_list, 10)  #   10篇一页
    # 获取页码参数(GET请求)  .GET 字典 使用get方法判断是否有page值,没有返回1
    page_num = request.GET.get("page", 1)
    page_of_articles = paginator.get_page(page_num) #   输入许可字符范围外的字符会返回1


    context = {}
    # context['articles'] = page_of_articles.object_list  #   前端页面
    context['page_of_articles'] = page_of_articles
    context['article_types'] = ArticleType.objects.all()
    return render(request, 'articles_list.html', context)

修改articles_list.html

{% block article_list_title %}Blog List | {{ page_of_articles.paginator.count }} blogs{% endblock %}
{% for article in page_of_articles.object_list %}

<!-- 页码 -->
<nav aria-label="Page navigation">
    <ul class="pagination">
        <li>
            {% if page_of_articles.has_previous %}
            <a href="?page={{ page_of_articles.previous_page_number }}" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
            {% else %}
            <span aria-hidden="true">&laquo;</span>
            {% endif %}
        </li>
        {% for page_index in page_of_articles.paginator.page_range %}
        <li><a href="?page={{ page_index }}">{{ page_index}}</a></li>
        {% endfor %}
        <li>
            {% if page_of_articles.has_next %}
            <a href="?page={{ page_of_articles.next_page_number }}" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
            </a>
            {% else %}
            <span aria-hidden="true">&raquo;</span>
            {% endif %}
        </li>
    </ul>
</nav>

5. 页面优化

1.    当前页高亮 class="active"
2.    不要一次性展示过多的页码选择,影响页面布局
3.    公共参数放入项目的settings.py中。

5.1 当前页高亮

 {% for page_index in page_range %}
                            {% if page_index == page_of_articles.number %}
                                <li class="active"><span>{{ page_index}}</span></li>

5.2 显示页码范围

views.py

def articles_list(request):
    articles_all_list = Articles.objects.all()
    paginator = Paginator(
        articles_all_list, settings.ARTICLE_NUMBER_EACH_PAGE)  # 4篇一页
    # 获取页码参数(GET请求)  .GET 字典 使用get方法判断是否有page值,没有返回1
    page_num = request.GET.get("page", 1)
    page_of_articles = paginator.get_page(page_num) #   输入许可字符范围外的字符会返回1

    # 获取当前页
    current_page = page_of_articles.number
    # 页码显示范围
    page_range = [i for i in range(current_page-2,current_page+3) if 0<i<=paginator.num_pages]
    # 第一页
    if page_range[0] >= 2:
        page_range.insert(0, 1)  # 第一位插入1页码
        if page_range[1] != 2:
            page_range.insert(1,'...')        # 加上...
    # 最后一页
    if page_range[-1] <= paginator.num_pages - 1:
        if page_range[-1] != paginator.num_pages - 1:
            page_range.append('...')        # 加上...
        page_range.append(paginator.num_pages)
def article_with_type(request, art_type_pk):
    article_type = get_object_or_404(ArticleType, pk=art_type_pk)

    articles_all_list = Articles.objects.filter(article_type=article_type)
    paginator = Paginator(
        articles_all_list, settings.ARTICLE_NUMBER_EACH_PAGE)  # 4篇一页
    # 获取页码参数(GET请求)  .GET 字典 使用get方法判断是否有page值,没有返回1
    page_num = request.GET.get("page", 1)
    # 输入许可字符范围外的字符会返回1
    page_of_articles = paginator.get_page(page_num)

    # 获取当前页
    current_page = page_of_articles.number
    # 页码显示范围
    page_range = [i for i in range(
        current_page-2, current_page+3) if 0 < i <= paginator.num_pages]
    # 第一页
    if page_range[0] >= 2:
        page_range.insert(0, 1)  # 第一位插入1页码
        if page_range[1] != 2:
            page_range.insert(1, '...')
    # 最后一页
    if page_range[-1] <= paginator.num_pages - 1:
        if page_range[-1] != paginator.num_pages - 1:
            page_range.append('...')
        page_range.append(paginator.num_pages)

    context = {}
    # context['articles'] = page_of_articles.object_list  #   前端页面
    context['page_of_articles'] = page_of_articles  # 当前页码
    context['article_type'] = article_type
    context['article_types'] = ArticleType.objects.all()
    context['page_range'] = page_range
    return render(request, 'articles_list.html', context)

articles_list.html

<div class="panel-heading">
    {% block article_list_title %}Blog List | {{ page_of_articles.paginator.count }} blogs{% endblock %}
</div>

<!-- 页码 -->
<nav aria-label="Page navigation">
    <ul class="pagination">
        <li>
            {% if page_of_articles.has_previous %}
            <a href="?page={{ page_of_articles.previous_page_number }}" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
            {% else %}
            <span aria-hidden="true">&laquo;</span>
            {% endif %}
        </li>
        {% for page_index in page_range %}
        {% if page_index == page_of_articles.number %}
        <li class="active"><span>{{ page_index}}</span></li>
        {% else %}
        {% if page_index == '...' %}
        <li><span>{{page_index}}</span></li>
        {% else %}
        <li><a href="?page={{ page_index }}">{{ page_index}}</a></li>
        {% endif %}
        {% endif %}
        {% endfor %}
        <li>
            {% if page_of_articles.has_next %}
            <a href="?page={{ page_of_articles.next_page_number }}" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
            </a>
            {% else %}
            <span aria-hidden="true">&raquo;</span>
            {% endif %}
        </li>
    </ul>
</nav>

5.3 全局参数放入settings.py

# 自定义参数...............................................................................
ARTICLE_NUMBER_EACH_PAGE = 4

调用全局参数from django.conf import settings