python列表分页

列表分页

上章的结束,若在实际开发过程中,会发现一个问题,那就首页或关注分享,是一下子按时间顺序全部显示出来,这在实际项目中不可能出现的,想想实际中的产品是如何做的?

一般来说,无非是两种,一种是使用页码,来进行分页,还有一种是js到页底自动加载,而使用页底自动加载的话,上一章实现的通过tab来区分全部和关注就不可取了,因为无法保证两个tab加载的内容数量一致,导致页面布局就无法实现,所以,这里首页参考tumblr的实现方式,删除关注分享的部分,只保留全部分享,使用js页底动态加载分页方式,同时在导航栏新增两个导航,分别为博文,和关注,使用传统页码的方式显示全部博文和已关注博文,这样是为了有些人可能会查询比较久的历史信息,所以,一个页面,一个功能如何设计,主要取决于业务需求,而不是技术需求。首先修改导航(base.html):

 <ul class="nav navbar-nav">
    <li><a href="/">首页</a></li>
    <li><a href="#">分享</a></li>
    {% if current_user.is_authenticated %}
    <li><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
           aria-haspopup="true" aria-expanded="false">关注 <span class="caret"></span></a>
      <ul class="dropdown-menu">
        <li><a href="#">分享</a></li>
        <li><a href="#">用户</a></li>
      </ul>
    </li>
    {% endif %}
  </ul>

用户登录后,在首页后面会新增两个item,分别是分享和关注,其中关注是一个下拉菜单,分别是“我”关注的用户发布的分享,和“我”关注的用户

下面完成这几个页面,首先是分享页,即所有用户发布的分享,页面与之前的首页很像,首先完成视图模型:

@main.route("/post")
@main.route("/post/<int:page>")
def post(page=1):
    pagination=Post.query.order_by(Post.createtime.desc()).paginate(
        page,per_page=current_app.config["POSTS_PER_PAGE"],error_out=False
    )
    return render_template("posts.html",posts=pagination.items,pagination=pagination,endpoint=request.endpoint)

这个模型的route的意思是,既可以通过/post访问,也可以通过/post/1等类型访问,当/post访问的时候,默认访问第一页。

endpoint的意思为访问的端点,即方法的端点,针对于这个方法来说,endpoint的值为"main.post"

接下来的内容,就是本章的一个重点了,pagination对象,这个是flask-SQLAlchemy框架中的一个很重要的对象,它包含了一系列用于分页的属性,其中主要的属性如下:

has_next
是否还有下一页

has_prev
是否还有前一页

items
当前页的数据

iter_pages(left_edge=2,left_current=2,right_current=5,right_edge=2)
一个关于页面的迭代,可以有四个带有默认值的参数:

  1. left_edge 页码最左边显示的页数
  2. left_current 当前页左边显示的页数
  3. right_current 当前页右边显示的页数
  4. right_edge 页面最右边显示的页数

可能有些不好理解,举个例子,假设共100页,当前为50页,则显示如下:

1,2...48,49,50,51,52,53,54,55...99,100

next(error_out=False)
下一页的分页对象,当error_out为true时,超过页面返回404
prev(error_out=False)
上一页的分页对象
page
当前页码
prev_num
上页页码
next_num
下页页码
per_page
每页显示的记录数量
total
记录总数
还有更多属性,请查验文档

分享页的模板与首页几乎一样,同样是一个分享发布框,一个已分享列表(posts.html):

{% import "_index_post_macros.html" as macros %}
...
<div class="container">
<div class="row">
 <div class="col-xs-12 col-md-8 col-md-8 col-lg-8">
  <div>
      {% if current_user.is_authenticated %}
      {{ wtf.quick_form(form) }}
      {% endif %}
  </div>
  <br>

<div  class="tab-content">
  <!--全部-->
  <div id="all" role="tabpanel" class="tab-pane fade in active">
    {{macros.rander_posts(posts,moment,pagination,endpoint)}}
  </div>
</div>
 </div>
 <div class="col-md-4 col-md-4 col-lg-4">
     <!--这里 当没有用户登录的时候 显示热门分享列表 稍后实现-->
     {% if current_user.is_authenticated %}
    <img src="http://on4ag3uf5.bkt.clouddn.com/{{current_user.headimg}}" alt="..." class="headimg img-thumbnail">
     <br><br>
     <p class="text-muted">我已经分享<span class="text-danger">{{ current_user.posts.count() }}</span>条心情</p>
     <p class="text-muted">我已经关注了<span class="text-danger">{{ current_user.followed.count() }}</span>名好友</p>
     <p class="text-muted">我已经被<span class="text-danger">{{ current_user.followers.count() }}</span>名好友关注</p>
     {%endif%}
 </div>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
{{ pagedown.include_pagedown() }}
<script type="text/javascript">
    $(‘.nav-tabs a‘).click(function (e) {
      e.preventDefault()
      $(this).tab(‘show‘)
    })
</script>
{% endblock%}

并没有做过多的封装,其实完全可以把右侧在封装成为一个macro

接下来是_index_post_macros.html

{% macro rander_posts(posts,moment,pagination=None,endpoint=None) %}
{% import "_posts_page_macros.html" as macros %}
{% for post in posts %}
  <div class="bs-callout
          {% if loop.index % 2 ==0 %}
           bs-callout-d
          {% endif %}
          {% if loop.last %}
            bs-callout-last
          {% endif %}" >
      <div class="row">
          <div class="col-sm-2 col-md-2">
                <!--使用测试域名-->
               <a class="text-left" href="{{url_for(‘main.user‘,username=post.author.username)}}">
                <img src="http://on4ag3uf5.bkt.clouddn.com/{{post.author.headimg}}" alt="...">
               </a>
          </div>
          <div class="col-sm-10 col-md-10">
           <div>
            <p>
               {% if post.body_html%}
                  {{post.body_html|safe}}
                {% else %}
               {{post.body}}
               {% endif %}
            </p>
            </div>
           <div>
            <a class="text-left" href="{{url_for(‘main.user‘,username=post.author.username)}}">{{post.author.nickname}}</a>
            <span class="text-right">发表于&nbsp;{{ moment( post.createtime).fromNow(refresh=True)}}</span>
           </div>
          </div>
      </div>
  </div>
{% endfor %}
{% if pagination and endpoint %}
  {{macros.rander_page(pagination,endpoint)}}
{% endif %}
{%endmacro%}

这里需要注意的一点也就是最下边新增的代码,意味着macro也可以嵌套,如果pagination和endpoint不为None,则显示页码,而_posts_page_macros.html的代码如下:

{% macro rander_page(pagination,endpoint) %}
<nav aria-label="Page navigation">
  <ul class="pagination  pagination-sm">
      {% if pagination.has_prev %}
      <li>
          <a href="{{url_for(endpoint,page=pagination.page-1)}}" aria-label="Previous">
               <span aria-hidden="true">&laquo;</span>
          </a>
       </li>
      {% else %}
      <li class="disabled">
          <a href="#" aria-label="Previous">
               <span aria-hidden="true">&laquo;</span>
          </a>
      </li>
      {% endif %}
    {% for p in pagination.iter_pages() %}
      {% if p%}
        {% if p ==pagination.page%}
            <li class="active"><a href="#">{{p}}</a></li>
        {% else %}
            <li><a href="{{url_for(endpoint,page=p)}}">{{p}}</a></li>
        {% endif %}
      {% else %}
        <li class="disabled"><a href="#">...</a></li>
      {% endif %}
    {% endfor %}

    {% if pagination.has_next %}
     <li>
      <a href="{{url_for(endpoint,page=pagination.page+1)}}"  aria-label="Next">
          <span aria-hidden="true">&raquo;</span>
      </a>
     </li>
    {% else %}
      <li class="disabled">
      <a href="#" aria-label="Next">
          <span aria-hidden="true">&raquo;</span>
      </a>
     </li>
    {% endif %}
  </ul>
</nav>
{% endmacro %}

这是一个比较典型的pagination的使用方式,完全使用了bootstrap的样式,最终的显示效果如下:

貌似内容有点少,分页我发测试,并且之后关注分页,首页动态分页都要用,所以首先要扩充一些分享内容,扩充的方式多种多样,比如实际数据,手动修改数据库,但对于python来说,它提供了一个不错的生成虚拟数据的轮子,即ForgeryPy,首先当然还是安装:

pip3.6 install ForgeryPy

然后修改Post类,添加一个静态方法(Post.py)

@staticmethod
def generate_fake():
    from random import seed, randint;
    from .User import User
    import forgery_py;
    seed()
    user_count = User.query.count()
    for i in range(100):
        u = User.query.offset(randint(0, user_count - 1)).first()
        p = Post(body=forgery_py.lorem_ipsum.sentences(randint(1, 3)),
                createtime=forgery_py.date.date(True), author=u)
        db.session.add(p)
        db.session.commit()

几个参数说明一下:

  1. lorem_ipsum比较有趣,原意为一些排版时的占位用的无意义字符,具体解释可参考阮博的博客,sentences方法为生成普通句子,参数代表句子的数量。
  2. date的参数表示生成时间的区间,True表示生成的区间都为过去的时间

这个静态方法的使用方式为:

python manage.py shell
Post.generate_fake()

这样就会生成100条分享。

下面在看一下分享页的效果(尾页):

不考虑美工的话,效果还是可以的,不过内容都是英文的,不知道能不能有一个中文的虚拟数据生成器:)

下面是关注分享页,和这个页类似,视图模型为:

@main.route("/follow_post",methods=["GET","POST"])
@main.route("/follow_post/<int:page>",methods=["GET","POST"])
@login_required
def follow_post(page=1):
    form = PostForm()
    if form_util(form):
        return redirect(url_for(request.endpoint))  # 跳回首页
    print(form.body.data)
    pagination=Post.query.select_from(Follow).filter_by(follower_id=current_user.id)    .join(Post,Follow.followed_id == Post.author_id).paginate(
        page,per_page=current_app.config["POSTS_PER_PAGE"],error_out=False
    )
    return render_template("posts.html",posts=pagination.items,form=form,
                          pagination=pagination,endpoint=request.endpoint)

注意,由于关注分享,分享和首页都有PostForm,所以把这个功能独立出来:

def form_util(form):
    if form.validate_on_submit():
        post = Post(body=form.body.data, author_id=current_user.id)
        db.session.add(post);
        return True
    return False

其实我想flask应该有整个页面的某一个功能独立为一个视图模型的方式,但我没有找到,如果读者找到了别忘了留言回复

最后,关注的用户就太简单了,可以直接使用某用户关注的页面,将userid参数赋予当前用户的参数即可,最终base.html的导航部分代码为:

<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
  <ul class="nav navbar-nav">
    <li><a href="/">首页</a></li>
    <li><a href="{{url_for(‘main.post‘)}}">分享</a></li>
    {% if current_user.is_authenticated %}
    <li><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
           aria-haspopup="true" aria-expanded="false">关注 <span class="caret"></span></a>
      <ul class="dropdown-menu">
        <li><a href="{{url_for(‘main.follow_post‘)}}">分享</a></li>
        <li><a href="{{url_for(‘main.follow_list‘,type=‘followed‘,userid=current_user.id)}}">用户</a></li>
      </ul>
    </li>
    {% endif %}
  </ul>
  <ul class="nav navbar-nav navbar-right">
    {% if current_user.is_authenticated %}
        <li><p class="navbar-text"><a href="#" class="navbar-link">{{current_user.username}}</a>  您好</p></li>
        <li><a href="{{url_for(‘auth.logout‘)}}">登出</a></li>
    {% else %}
        <li><a href="{{url_for(‘auth.login‘)}}">登录</a></li>
        <li><a href="{{url_for(‘auth.register‘)}}">注册</a></li>
    {% endif %}
  </ul>
  <form class="navbar-form navbar-right">
    <div class="form-group">
      <input type="text" class="form-control" placeholder="Search">
    </div>
    <button type="submit" class="btn btn-default">搜索</button>
  </form>
</div><!-- /.navbar-collapse -->

然后删除首页中tab的部分,最终代码为:

<div class="container">
<div class="row">
 <div class="col-xs-12 col-md-8 col-md-8 col-lg-8">
  <div>
      {% if current_user.is_authenticated %}
      {{ wtf.quick_form(form) }}
      {% endif %}
  </div>
  <br>
  <ul class="nav nav-tabs">
      <li role="presentation" class="active"><a href="#all">最新分享</a></li>
  </ul>
<div  class="tab-content">
  <!--全部-->
  <div id="all" role="tabpanel" class="tab-pane fade in active">
    {{macros.rander_posts(posts,moment)}}
  </div>
</div>
 </div>
 <div class="col-md-4 col-md-4 col-lg-4">
     <!--这里 当没有用户登录的时候 显示热门分享列表 稍后实现-->
     {% if current_user.is_authenticated %}
    <img src="http://on4ag3uf5.bkt.clouddn.com/{{current_user.headimg}}" alt="..." class="headimg img-thumbnail">
     <br><br>
     <p class="text-muted">我已经分享<span class="text-danger">{{ current_user.posts.count() }}</span>条心情</p>
     <p class="text-muted">我已经关注了<span class="text-danger">{{ current_user.followed.count() }}</span>名好友</p>
     <p class="text-muted">我已经被<span class="text-danger">{{ current_user.followers.count() }}</span>名好友关注</p>
     {%endif%}
 </div>
</div>
</div>

这时候,你可能已经发现了,首页的分享还没有进行分页,在本章的开始部分就已经解释道,首页使用动态加载的分页方式,而动态加载显然需要js的配合,使用json的方式向html中注入。这时候服务端就会面临一个问题,如何与客户端的js进行交互呢,这是下一章将要说明的问题。

时间: 2024-10-07 22:30:22

python列表分页的相关文章

python/Djangof分页与自定义分页

python/Djangof分页与自定义分页 Django分页 1 ##============================================分页======================================================= 2 # current_pagc = request.GET.get('page') 3 # from django.core.paginator import Paginator,Page 4 # # result = m

python列表和QVariant

pyqt中,要给QAbstractTableModel的setData函数传递一个list参数: [20,'00:00:19'] 涉及到QVariant和list的转换. 可以使用QVariant类中的toPyObject是转换. 环境是:Python 2.7.6 pyqt4 4.8.6 有文章说是,toPyObject只能转换字符串,而且只能转换字典. 测试一下,支持数字,支持字典和列表. #coding:utf-8 from PyQt4.QtCore import QVariant a={2

Python列表的增删改查排

Python列表的增删改查排 一.列表的样子: a = ['q' , 'w' , 'e ', 'r','t'] a为列表名,[ ]为列表内容,' '为列表内的元素,'q'为a[0] 二.查(也称切片): print ( a[0] )          #取出列表中第一个元素 即:q print ( a[1:] )        #从第二个元素开始取到最后 即:['w', 'e','r', 't'] print ( a[1:3] )        #取出从第二个开始到第三个元素 即:['w','e

python列表和元组的方法和属性

python序列的共性 python序列包括列表和元组,序列都有索引,第一个元素索引为0,序列都可以进行索引.加.乘.切片.检查成员. python列表 列表是最常用的数据类型之一,通过[,,,]建立,可以给变量赋值空列表[].列表的数据项可以是不同类型的,可以嵌套. 列表的索引和字符串操作一样,同样可以截取例如a[1:2] / a[1:] / a[:3] 列表的更新: 1.修改:通过索引直接赋值来修改列表中的值. 2.新增:通过list.append(),在末尾处添加元素. 3.删除: A.通

[转载] Python 列表(list)、字典(dict)、字符串(string)常用基本操作小结

创建列表 sample_list = ['a',1,('a','b')] Python 列表操作 sample_list = ['a','b',0,1,3] 得到列表中的某一个值 value_start = sample_list[0] end_value = sample_list[-1] 删除列表的第一个值 del sample_list[0] 在列表中插入一个值 sample_list[0:0] = ['sample value'] 得到列表的长度 list_length = len(sa

嗨分享-前端技术-帝国CMS手机站修改列表分页(sysShowListMorePage)

http://bbs.phome.net/showthread-31-318753-0.html 如果你的网站使用的是帝国CMS.PC站和手机站各使用一个模板组,但共同使用一个数据库.那么你的PC站和手机站,列表分页样式是一样的吗? 今天<a href="http://www.thy6415.com">嗨分享-前端技术</a>将带你感受不一样的分页样式! PC站可以把分页按1.2.3.4...这样并排 列出来,但手机站是万万不可以这样做的,否则手机屏幕小,分页看

帝国CMS手机站修改列表分页(sys_ShowListMorePage)

如果你的网站使用的是帝国CMS.PC站和手机站各使用一个模板组,但共同使用一个数据库.那么你的PC站和手机站,列表分页样式是一样的吗? 今天嗨分享-前端技术将带你感受不一样的分页样式! PC站可以把分页按1.2.3.4...这样并排 列出来,但手机站是万万不可以这样做的,否则手机屏幕小,分页看着都好长,一点都不美观. 帝国CMS的PC站可以使用[!--show.listpage--]调用列表形式的列表分页函数(sys_ShowListMorePage). 一旦PC站使用[!--show.list

python 列表推导

python 列表推导 python List Comprehensions List Comprehensions List comprehensions are an elegant way to build a list without having to use different for loops to append values one by one. 最简单形式 [ expression-involving-loop-variable for loop-variable in s

Python 列表(list)操作

创建列表 sample_list = ['a',1,('a','b')] Python 列表操作 sample_list = ['a','b',0,1,3] 得到列表中的某一个值 value_start = sample_list[0] end_value = sample_list[-1] 删除列表的第一个值 del sample_list[0] 在列表中插入一个值 sample_list[0:0] = ['sample value'] 得到列表的长度 list_length = len(sa