利用SQLAlchemy和Bootstrap实现数据分页显示

20 篇文章 1 订阅
16 篇文章 7 订阅

随着Web业务拓展,数据量会不断增长,如果在一个页面上将全部数据一次性显示出来,服务器和浏览器都会受到很大影响,不切合实际。一般的解决方式是采用分页显示的办法。在Flask框架中,可以利用SQLAlchemy实现分页查询,结合Bootstrap提供的强大CSS分页效果,完美地实现分页导航的效果。

0x01 创建虚拟数据

为了更好地体现显示分页效果,我们先在数据模型中虚拟足够的模拟数据。在Python中,我们可以利用Forgerypy来实现(参见利用ForgeryPy生成虚拟数据)。下面我们直接写两个函数来实现实验数据的生成:

Users.py:

#生成模拟数据
@staticmethod
def generate_fake(count=100):
    from sqlalchemy.exc import IntegrityError
    from random import seed
    import forgery_py

    seed()
    for i in range(count):
        u=Users()
        u.username=forgery_py.internet.user_name(True)
        u.email=forgery_py.internet.email_address()
        u.password=forgery_py.lorem_ipsum.word()
        u.confirmed=True
        u.about_me=forgery_py.lorem_ipsum.sentence()
        u.realname=forgery_py.name.full_name()
        u.location=forgery_py.address.city()
        u.registerTime=forgery_py.date.date(True)
        db.session.add(u)
        try:
            db.session.commit()
        except IntegrityError:
            #回滚
            db.session.rollback()

Posts.py:

@staticmethod
def generate_fake(c=100):
    from random import randint,seed
    import forgery_py
    from Users import Users

    seed()
    count=db.session.query(Users).count()
    for i in range(c):
        u=db.session.query(Users).offset(randint(0,count-1)).first()
        p=Posts(author=u)
        p.body=forgery_py.lorem_ipsum.sentence()
        p.timestamp=forgery_py.date.date(True)
        db.session.add(p)
        db.session.commit()

0x02 渲染数据

定义支持分页显示的路由:

#分页显示
page=request.args.get('page',1,type=int)
pagination=Posts.query.order_by(Posts.timestamp.desc()).paginate(
    page,per_page=5,
    error_out=False
)
posts=pagination.items
return render_template('main/index.html',posts=posts,form=form,pagination=pagination)

通过request.args.get获取需要显示的页码(type定义为整数,异常默认为1),per_page定义了每页显示的记录的个数。error_out参数如果设置成True,当请求的页数超过了总的页数范围,就会返回一个404错误,如果设为False,就会返回一个空列表。这样修改后,页面只会显示有限的记录数量。

0x03 添加分页导航

paginate()方法返回了一个Pagination对象。这个类在Flask-SQLAlchemy中定义,包含大量属性和方法,可以非常方便地生成分页导航模型。

属性:

序号属性说明
1items当前页的记录个数
2query分页的源查询
3page当前的页数
4prev_num上一页数
5next_num下一页数
6has_prev是否有上一页
7has_next是否有下一页
8pages总的页数
9per_page每页显示的记录条数
10total总的记录条数

方法:

序号方法说明
1iter_pages(left_edge=2,left_current=2,right_current=5,right_age=2)一个迭代器,返回选中当前页时显示的页数列表。如这个例子中最左边显示2个页数,当前页的左边显示2个页数,当前页的右边显示5个页数而当前页的最右边显示2个页数
2prev()上一页的分页对象
3next()下一页的分页对象

结合Bootstrap提供的分页CSS效果,我们可以实现一个美观的分页导航。定义一个macro宏(/templates/_macros.html):

{% macro pagination_widget(pagination, endpoint, fragment='') %}
<div class="pagination">
    <ul class="pagination">
    <li{% if not pagination.has_prev %} class="disabled"{% endif %}>
        <a href="{% if pagination.has_prev %}{{ url_for(endpoint, page=pagination.prev_num, **kwargs) }}{{ fragment }}{% else %}#{% endif %}">
            &laquo;
        </a>
    </li>
    {% for p in pagination.iter_pages() %}
        {% if p %}
            {% if p == pagination.page %}
            <li class="active">
                <a href="{{ url_for(endpoint, page = p, **kwargs) }}{{ fragment }}">{{ p }}</a>
            </li>
            {% else %}
            <li>
                <a href="{{ url_for(endpoint, page = p, **kwargs) }}{{ fragment }}">{{ p }}</a>
            </li>
            {% endif %}
        {% else %}
        <li class="disabled"><a href="#">&hellip;</a></li>
        {% endif %}
    {% endfor %}
    <li{% if not pagination.has_next %} class="disabled"{% endif %}>
        <a href="{% if pagination.has_next %}{{ url_for(endpoint, page=pagination.next_num, **kwargs) }}{{ fragment }}{% else %}#{% endif %}">
            &raquo;
        </a>
    </li>
</ul>
</div>
{% endmacro %}

可以在视图模板的适当位置引入这个宏:

{%extends 'base.html'%}
{%import 'bootstrap/wtf.html' as wtf%}
{%import '_macros.html' as macros%}

……

{%if pagination%}
{{macros.pagination_widget(pagination,'main.index')}}
{%endif%}

这样就实现了分页导航的效果:

0x04 功能拓展

我们可以对分页导航进行拓展(参见Bootstrap学习总结笔记(12)– 基本组件之分页)。

_macros.html中添加跳转功能:

{% macro pagination_widget(pagination, endpoint, fragment='') %}
<div class="pagination">
    <ul class="pagination">
    <li{% if not pagination.has_prev %} class="disabled"{% endif %}>
        <a href="{% if pagination.has_prev %}{{ url_for(endpoint, page=pagination.prev_num, **kwargs) }}{{ fragment }}{% else %}#{% endif %}">
            &laquo;
        </a>
    </li>
    {% for p in pagination.iter_pages() %}
        {% if p %}
            {% if p == pagination.page %}
            <li class="active">
                <a href="{{ url_for(endpoint, page = p, **kwargs) }}{{ fragment }}">{{ p }}</a>
            </li>
            {% else %}
            <li>
                <a href="{{ url_for(endpoint, page = p, **kwargs) }}{{ fragment }}">{{ p }}</a>
            </li>
            {% endif %}
        {% else %}
        <li class="disabled"><a href="#">&hellip;</a></li>
        {% endif %}
    {% endfor %}
    <li{% if not pagination.has_next %} class="disabled"{% endif %}>
        <a href="{% if pagination.has_next %}{{ url_for(endpoint, page=pagination.next_num, **kwargs) }}{{ fragment }}{% else %}#{% endif %}">
            &raquo;
        </a>
    </li>
    <li class="pull-right">
        <label for="transfer_page">跳转到:</label>
        <input type="text" class="text-info" size="3" id="transfer_page">
        <button type="button" class="btn btn-primary btn-sm" id="transfer">Go</button>
    </li>
</ul>
</div>
{% endmacro %}

在视图模板中加入js代码:

{%block scripts%}
{{super()}}
<script type="text/javascript">
    $(function(){
        $('#transfer').click(function(){
            var page=$('#transfer_page').val();
            window.location.href="{{url_for('main.index')}}"+"?page="+page;
        })
    })
</script>

效果如下:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值