sqlalchemy 和 flask-sqlalchemy 分表

对于 sqlalchemy 和 flask-sqlalchemy 分表的方案,网上的资料很少,经过google和自己项目的尝试,找到比较合适的解决的方案。 下面的 user 表根据用户的ID % 100 分布在 0 - 99 张表中。 另外,如果要处理分库的话,只需要在model中加入 __bind_key__,等于分库的规则。 sqlalchemy 分表示例: #!/usr/bin/env python # -*- coding: utf-8 -*- from sqlalchemy import Column, Integer, String, BigInteger, SmallInteger from sqlalchemy import create_engine from sqlalchemy.orm import Session from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class UserData(object): _mapper = {} @staticmethod def model(user_id): table_index = user_id % 100 class_name = 'user_%d' % table_index ModelClass = UserData._mapper.get(class_name, None) if ModelClass is None: ModelClass = type(class_name, (Base,), dict( __module__=__name__, __name__=class_name, __tablename__='user_%d' % table_index, id=Column(BigInteger, primary_key=True), name=Column(String(255)), picture=Column(String(255)), valid=Column(SmallInteger), utime=Column(Integer), ctime=Column(Integer) )) UserData._mapper[class_name] = ModelClass cls = ModelClass return cls if __name__ == '__main__': engine = create_engine('mysql+pymysql://%s:%s@%s:%s/%s' % ('root', 'root', '127.0.0.1', '3306', 'test'), encoding='utf8') Base.metadata.create_all(engine) session = Session(engine) # user_id = 100001 user_id = 1000001 user = UserData.model(user_id) result_1 = session.query(user).count() result_2 = session.query(user).filter(user.name == "test", user.valid == 0).all() result_3 = session.query(user).filter_by(name="test", valid=0).all() print(result_1, result_2, result_3) flask-sqlalchemy 分表示例: #!/usr/bin/env python # -*- coding: utf-8 -*- from flask import Flask, jsonify from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config.from_pyfile('flask-sqlalchmey-sharding.cfg') db = SQLAlchemy(app) class User(object): '''用户分表''' _mapper = {} @staticmethod def model(user_id): table_index = user_id % 100 # db_index = int((user_id % 100) / 25) class_name = 'user_%d' % table_index ModelClass = User._mapper.get(class_name, None) if ModelClass is None: ModelClass = type(class_name, (db.Model,), { '__module__': __name__, '__name__': class_name, '__tablename__': 'user_%d' % table_index, 'id': db.Column(db.BigInteger, primary_key=True), 'name': db.Column(db.String(100)), 'phone': db.Column(db.String(20)), 'status': db.Column(db.SmallInteger, default=0), 'ctime': db.Column(db.Integer, default=0), 'utime': db.Column(db.Integer, default=0) }) User._mapper[class_name] = ModelClass cls = ModelClass return cls @app.route('/ /test', methods=['GET', 'POST']) def test(user_id): user = User.model(user_id) count = user.query.filter_by(id=user_id).count() item = user.query.filter(user.id == user_id, user.status == 0).first() # default name = item.name if item else "default" return jsonify(count=count, name=name) if __name__ == '__main__': app.run() flask-sqlalchmey-sharding.cfg 文件: SQLALCHEMY_DATABASE_URI = = 'mysql+pymysql://root:root@localhost:3306/test' SQLALCHEMY_ECHO = True SECRET_KEY = '\xfb\x12\xd ...

阅读全文 »

flask+sqlalchemy+postgresql 异步方案

flask+sqlalchemy+postgresql 异步方案的Github 地址: https://github.com/sixu05202004/async-flask-sqlalchemy-postgresql 使用说明: 安装所需的插件: pip install -r requirements.txt 需要在postgresql中创建数据库 test_asyn 运行 python app.py -c 用来创建测试的表; 运行 python app.py 来启动服务器,运行 python client 来测试 结果如下:: localhost:async-flask-sqlalchemy-postgresql sixu05202004$ python client.py Sending 5 requests for http://localhost:8080/test/postgres/... @ 5.05s got response [200] @ 5.05s got response [200] @ 5.05s got response [200] @ 5.05s got response [200] @ 5.05s got response [200] = 5.06s TOTAL SUM TOTAL = 5.06s 注意: 1.config.py 中需要修改测试数据库的用户名和密码,并且可以修改pool_size的数量; 2.python client.py num,比如:python client.py 100可以用来模拟更多的连接; 3.如果python client.py num中得num数大于config.py中的SQLALCHEMY_POOL_SIZE = 100时候,我们会发现有些数据库连接又存在阻塞的情况。 比如,我们把SQLALCHEMY_POOL_SIZE改成10,使用python client.py 30来测试,结果如下:: localhost:async-flask-sqlalchemy-postgresql sixu05202004$ python client.py 30 Sending 30 requests for http://localhost:8080/test/postgres/... @ 5.07s got response [200] @ 5.07s got response [200] @ 5.08s got response [200] @ 5.09s got response [200] @ 5.09s got response [200] @ 5.13s got response [200] @ 5.12s got response [200] @ 5.12s got response [200] @ 5.13s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.20s got response [200] @ 5.20s got response [200] @ 5.20s got response [200] @ 10.14s got response [200] @ 10.15s got response [200] @ 10.15s got response [200] @ 10.24s got response [200] @ 10.24s got response [200] @ 10.24s got response [200] @ 10.24s got response [200] @ 10.25s got response [200] @ 10.25s got response [200] @ 10.26s got response [200] = 10.28s TOTAL SUM TOTAL = 10.28s 这是因为 连接数 》SQLALCHEMY_POOL_SIZE + SQLALCHEMY_MAX_OVERFLOW ,我们可以通过一些设置来避免这种情况(NullPool ),但是实际上 postgresql 规定了最大连接数,这个是无法避免的,因此上述的设置最好不要使用 ...

阅读全文 »

flask-cache源码简要分析

flask-cache源码文件包含下面三个文件:
  1. __init__.py:flask-cache主要功能,提供了对用户使用的各种函数;
  2. backends.py:对werkzeug.contrib.cache进行一些封装以及扩展,为__init__.py提供多种缓存类型;
  3. jinja2ext.py:jinjia2的扩展,用于缓存jinjia2模版;

jinja2ext.py

首先我们来简要剖析下flask-cache扩展jinjia2,用于缓存jinjia2模版片段。该文件提供一个类:CacheExtension,该类的父类是jinjia2.ext.Extension,在父类的基础上对parse(self, parser)进行了扩展(修改)。具体的代码需要对jinjia2有些了解,尤其是jinjia2.ext.Extension,这里不啰嗦。

backends.py

这里包含了flask-cache对外提供的内置的缓存类型:NullCache、SimpleCache、MemcachedCache 、GAEMemcachedCache、RedisCache 、FileSystemCache、SASLMemcachedCache。

  • NullCache是对werkzeug.contrib.cache中的NullCache进行简单封装;
  • SimpleCache是对werkzeug.contrib.cache中的SimpleCache进行简单封装;
  • MemcachedCache是对werkzeug.contrib.cache中的MemcachedCache进行简单封装;
  • GAEMemcachedCache是对werkzeug.contrib.cache中的GAEMemcachedCache进行简单封装;
  • RedisCache是对werkzeug.contrib.cache中的RedisCache进行简单封装;
  • FileSystemCache是对werkzeug.contrib.cache中的FileSystemCache进行简单封装;
  • SASLMemcachedCache是flask-cache扩展了werkzeug.contrib.cache中的基础类MemcachedCache,实现了一个值跨多个不同的key存储;

__init__.py

文件里面只包含一个类:Cache。该类最重要的两个函数:cached和memoize,都能作为装饰器使用。cached主要用于缓存视图函数或者非视图函数,而memoize是Memoization <http://en.wikipedia.org/wiki/Memoization>的一种体现,主要用于缓存函数结果,这样多次调用的函数,只需要计算一次。

阅读全文 »

FLASK开源项目--派生吧博客推介之五

数据库的部分已经介绍完成,现在介绍了最后一部分:视图函数。 对于博客而言,视图函数需要完成的是:首页,文章页,分类页,标签页,搜索页,错误页面,关于页面。 错误页面 这个部分代码十分简单,直接返回404的模板。 @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404 @app.route('/error/404') def error_404(): return render_template('404.html'), 404 关于页面 直接返回about页面的模板,直接需要额外处理页面的侧边栏,这个时候需要传一些参数:所有的分类,热门文章,最新文章,随机便签,最新评论。 @app.route('/about') def about(): categorys = Category.query.getall() #所有的分类 hot = Post.query.hottest()[:20] #热门文章,20个 new = Post.query.newpost()[:20] #最新文章,20个 tag = Tag.query.getall() shuffle(tag) tag = tag[:20] #标签云,20个 comments = Comment.query.newcomment()[:20] #最新评论,20个 return render_template('/about.html', categorys=categorys, hotarticles=hot, newpost=new, tags=tag, comments=comments) 首页 对于首页需要关注的是分页的情况,幸好, flask-sqlalchemy 提供了相应的处理函数,这里需要处理的提供当前页面,最后页数的参数到模板中。 @app.route('/') @app.route('/page/') def index(pageid=1): categorys = Category.query.getall() p = Post.query.getpost_perpage(pageid, per_page) hot = Post.query.hottest()[:20] new = Post.query.newpost()[:20] tag = Tag.query.getall() shuffle(tag) tag = tag[:20] comments = Comment.query.newcomment()[:20] articles = p.items if not p.total: pagination = [0] elif p.total % per_page: pagination = range(1, p.total / per_page + 2) else: pagination = range(1, p.total / per_page + 1) return render_template('/index.html', categorys=categorys, articles=articles, hotarticles=hot, newpost=new, tags=tag, comments=comments, pageid=pageid, pagination=pagination[pageid - 1:pageid + 10], last_page=pagination[-1], nav_current="index" ) 标签,分类页面 这个其实跟首页雷同,只是需要提供的文章内容不同。代码就略了。 搜索页面 对于搜索页面,我们需要获取请求的搜索关键词,然后调用数据库的查询函数,传入关键字就行。 @app.route('/search') @app.route('/search/page/') def search(pageid=1): categorys = Category.query.getall() hot = Post.query.hottest()[:20] new = Post.query.newpost()[:20] tag = Tag.query.getall() shuffle(tag) tag = tag[:20] comments = Comment.query.newcomment()[:20] searchword = request.args.get('s', '') if not searchword: return redirect(url_for('error_404')) searchresult = Post.query.search(searchword) p = pageby(searchresult, pageid, per_page, Post.post_create_time.desc()) articles = p.items if not p.total: pagination = [0] elif p.total % per_page: pagination = range(1, p.total / per_page + 2) else: pagination = range(1, p.total / per_page + 1) return render_template('/search.html', key=searchword, categorys=categorys, articles=articles, hotarticles=hot, newpost=new, tags=tag, comments=comments, pageid=pageid, pagination=pagination[pageid - 1:pageid + 10], last_page=pagination[-1] ) 文章页 对于文章页面,只需要传入指定的文章内容就行了,侧边栏跟首页的处理方式一样。 @app.route('/article/') def article(postid=5): categorys = Category.query.getall() hot = Post.query.hottest()[:20] new = Post.query.newpost()[:20] tag = Tag.query.getall() shuffle(tag) tag = tag[:20] comments = Comment.query.newcomment()[:20] articles = Post.query.getall() shuffle(articles) articles = articles[:5] post = Post.query.getpost_id(postid) if not post: return redirect(url_for('error_40 ...

阅读全文 »

FLASK开源项目--派生吧博客推介之四

这一部分主要介绍博客的数据库以及定义的常用的查询函数,这一部分代码主要在model.py里面。博客的数据库主要包含category,tag,tags,post,comment五张表。现在分别对这五张表以及相关的查询函数进行解析: category category(分类表),主要储存了博客的分类,表项只有id(自增)以及name(唯一). class Category(db.Model): __tablename__ = 'category' #表名 query_class = CategoryQuery #指定的查询类名 id = db.Column(db.Integer, primary_key=True) #id category_name = db.Column(db.String(200), unique=True) #name #下面的内容可以自由选择 def __init__(self, category_name): self.category_name = category_name def __repr__(self): return ' ' % self.category_name 查询只要定义了两个函数getall以及getcategory_id:getall()获取所有的分类,getcategory_id(id)获取指定id 的分类。 class CategoryQuery(BaseQuery): def getall(self): return self.all() def getcategory_id(self, id): return self.get(id) tags tags(文章标签映射表),主要存储了文章id与标签的id的对应关系,存储文章的时候,该表会自动进行更新。 article_tags = db.Table('tags', db.Column( 'tag_id', db.Integer, db.ForeignKey('tag.id')), db.Column( 'post_id', db.Integer, db.ForeignKey('post.id')), ) tag tag(标签表),主要存储了文章的标签,表只有id(自增)以及name。 class Tag(db.Model): __tablename__ = 'tag' #表名 query_class = TagQuery #指定的查询类名 id = db.Column(db.Integer, primary_key=True) #id name = db.Column(db.String(50)) #name def __init__(self, name): self.name = name def __repr__(self): return ' ' % self.name 查询只要定义了两个函数getall以及gettag_id:getall()获取所有的标签,gettag_id(id)获取指定id 的标签。 class TagQuery(BaseQuery): def getall(self): return self.all() def gettag_id(self, id): return self.get(id) post post(文章表),主要存储了博客的文章内容以及相关的分类以及标签信息,表只有id(自增)、post_content、post_title、post_create_time、view_num、comment_count、status、author_id、post_modified_time、category_id、post_name、tags_name(id,文章内容,文章标题,文章创建时间,阅读次数,评论数量,文章状态,作者id,文章修改时间,分类id,网址的缩写名称,标签名称)。 class Post(db.Model): __tablename__ = 'post' #表名 query_class = PostQuery #指定的查询类名 id = db.Column(db.Integer, primary_key=True) #id post_content = db.Column(db.Text) #文章内容 post_title = db.Column(db.String(100)) #文章标题 post_name = db.Column(db.String(200), unique=True) #文章的网址的缩写,主要用于文章的网址以便于SEO优化 post_create_time = db.Column(db.DateTime, default=datetime.utcnow) #文章创建时间 view_num = db.Column(db.Integer, default=0) #阅读次数 comment_count = db.Column(db.Integer, default=0) #评论次数 status = db.Column(db.Integer, default=1) #文章状态 author_id = db.Column(db.Integer, default=1) #作者id,默认是1 post_modified_time = db.Column(db.DateTime, default=datetime.utcnow) #文章的修改时间 category_id = db.Column(db.Integer, db.ForeignKey('category.id')) #类型id categorys = db.relationship('Category', backref=db.backref( 'posts', lazy='dynamic'), lazy='select') #定义与category表之间的关系 tags = db.relationship('Tag', secondary=article_tags, backref=db.backref('posts', lazy='dynamic')) #定义域tag表之间的关系 def __init__(self, tags, post_content, post_title, category_id, post_name, post_create_time=None, view_num=0, comment_count=0, status=1, author_id=1, post_modified_time=None): self.post_content = post_content self.post_title = post_title self.category_id = category_id self.post_name = post_name ...

阅读全文 »

FLASK开源项目--派生吧博客推介之三

这一部分主要对博客的源代码进行就解析,会分三次完成。首先看看代码主要有config.py,model.py,views.py,myapp.py这四个文件。


  • config.py


# -*- coding: utf-8 -*-

# configuration page num  每页的文章数

PER_PAGE = 10        

# configuration mysql

SQLALCHEMY_DATABASE_URI = "mysql://%s:%s@%s/%s"%('root','root','127.0.0.1','test')
# 密钥
SECRET_KEY = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
# 管理界面的用户名和密码
USERNAME = 'admin'
PASSWORD = 'admin'
# 图片上传路径
UPLOAD_FOLDER = '/static/upload/'
#允许的图片类型

ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

配置文件里面主要设置了每页的文章数,数据库的相关信息:用户名、密码、地址、数据库名称,密钥,管理页面的用户名和密码,图片上传路径,允许的图片类型。

  • myapp.py

这里面的代码比较简单,主要是导入相应的模块,app运行的端口以及debug模式:


from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
from views import *

if __name__ == '__main__':
 app.run(host='0.0.0.0', debug=True)


阅读全文 »

flask开源项目--派生吧博客推介之二

上一部分介绍了博客整个结构,这一部分主要介绍templates与static两大文件夹里面的内容。

首先来看下templates 的内容:

这是整个博客的所有的页面:

  • 404.html:错误页面,里面嵌入了公益寻人的js代码;
  • about.html:个人说明页面
  • category.html:分类目录的页面
  • editpost.html:文章编辑的页面
  • error.html:错误页面,里面会显示具体的错误信息,用于给调试使用;
  • foot.html:所有页面的页脚部分
  • head.html:所有页面的头的部分
  • index.html:博客的首页
  • layout.hml:页面的通用结构
  • login.html:登录页面
  • newpost.html:编写新文章的页面
  • post.html:单页文章的页面
  • search.html:搜索结果的页面
  • sider.html:所有页面的侧边栏
  • tag.html:标签页面

static文件夹内容:

css:包含页面所有的css

images:页面的所有使用的图片

js:页面所使用的js

kindeditor:富媒体文本编辑器的代码地址

upload:博客上传的图片的文件夹

这是这两大文件夹里面的内容,下一部分针对所有源码就行解析!

阅读全文 »

flask开源项目--派生吧博客推介之一

项目简介:

网址:www.pythonpub.com

代码地址:https://github.com/sixu05202004/flaskblog

项目是用于简单的个人博客,前端界面主要是拷贝了wordpress的主题:NeoEase

项目使用的技术与插件:


  • flask_sqlalchemy:是一个给你的应用添加 SQLALchemy 支持的 Flask 扩展。 它致力于简化在 Flask 中 SQLAlchemy 的 使用,提供了有用的默认值和额外的助手来更简单地完成日常任务。
  • flask_cache:缓存插件,可以用于提高网页的访问速度
  • Bootstrap:Twitter的著名的CSS/HTML框架
  • kindeditor:富文本编辑器


代码结构:

templates里面包含了很多网页模板:首页,文章页,标签页,分类页,搜索页等等,views.py包含很多的试图函数,config.py里面提供了一些基本的配置,model.py提供了基本的数据模型以及一些数据查询和处理的函数。

下一部分将会对源代码进行解析。


阅读全文 »