最近在学习python web开发,flask web框架让python web开发变得非常简单。在学习《Flask Web开发实战》中的开源博客(blueblog)程序时发现有很多地方的写法值得商榷,都可以进行优化。
如在显示文章数和回复数的时候是在模板中直接通过对象列表的长度来获取如{{ category.posts|length }},这种方式实际是进行查询后再获取对象列表的长度,而这种查询是没有其他分页条件,会将所有的记录查出来。如果作为学习用作展示jinja2的length过滤器的功能还好说,用在实际的项目中将会引起很大的性能问题。如果某个类目下的文章很多以后,这种方式将会引起程序的崩溃。
获取分类下的文章数代码,
{% for category in categories %}
<li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
<a href="{{ url_for('blog.show_category', category_id=category.id) }}">
{{ category.name }}
</a>
<span class="badge badge-primary badge-pill"> {{ category.posts|length }}</span>
</li>
{% endfor %}
打开flask 的调试监控器可以清楚的看到循环类目列表并且每个类目都去查询了类目下文章的信息,如果在每个类目下的文章数据量小的时候还可以接受,数据量大了必将会引起程序崩溃。
能不能不用每次都去查询类目下的文章数呢?想到的办法是在类目表新增一个字段来记录该类目的文章数。但是如何保证文章数的准确性呢?那就用到了flask-SQLAlchemy触发器在新增文章或删除文章的时候同时更新类目中的文章数。具体如下:
1、在类目表新增字段来记录该类目的文章数
2、在文章类中增加触发需要调用的更新文章数目的类方法。
@staticmethod
def chang_post_count(category_id):
db.session.execute(text('update category set post_count=(select count(1) from post where category_id=:category_id) where id=:category_id'), {'category_id': category_id})
@staticmethod
def on_insert(mapper, connection, target):
Post.chang_post_count(target.category_id)
@staticmethod
def on_delete(mapper, connection, target):
Post.chang_post_count(target.category_id)
3、注册触发器的事件监听
db.event.listen(Post,'after_insert',Post.on_insert)
db.event.listen(Post,'after_delete',Post.on_delete)
这里就是说在Post的增加或删除时都会触发Post类的on_insert或on_delete方法去更新文章的数目。
4、最后将类目下的文章数改成直接通过实例的属性进行展示
{{ category.post_count }}
这样就通过flask-SQLAlchemy触发器实现了动态更新数据,避免反复查询的低效方式。虽然在新增或删除文章时会多了一步触发操作,但一般来说博客系统的更新操作的频率远远低于查询的频率,所以对于新增或删除来说不会有太大的影响。
最后效果:
可以看到循环的查询没有了,整个页面的执行时间从97毫秒变成了14毫秒,说明优化的效果还是蛮显著的。更重要的是避免了文章数据量多了后系统崩溃的隐患。
全部代码见github
https://github.com/xiejava1018/ishareblog
开源博客效果