flask 项目搭建及配置分享

作者Tom .Lee,GitHub ID:tomoncle ,Web and cloud computing developer, Java, Golang, Python enthusiast.

概要

好久没有碰flask框架了,近期写点东西,回忆一下,分享小伙伴入门flask,并分享源代码,见文章底部

  • 拓展flask支持banner, 支持config.properties配置文件导入

  • 模块化设计,支持数据库迁移

  • 封装sqlalchemy数据库操作

  • 自动转json

  • 配置拦截器,异常自动解析(web请求返回错误页面,curl请求返回错误json)

  • 拓展flask内置函数,支持环境变量

  • 集成celery框架异步处理

  • 支持docker构建

  • flask jinja2模板示例

  • swagger api文档配置

  • 等等

模块结构图

 
  
  1.    .

  2.    .

  3.    ├── banner.txt

  4.    ├── bootstrap_app.py

  5.    ├── bootstrap_celery.py

  6.    ├── config.properties

  7.    ├── config.py

  8.    ├── Dockerfile

  9.    ├── examples

  10.       ├── extensions_flask_form.py

  11.       ├── extensions_flask_SQLAlchemy.py

  12.       ├── hello_world.py

  13.       ├── index.py

  14.       ├── __init__.py

  15.       └── rest_api.py

  16.    ├── flaskapp

  17.       ├── common

  18.          ├── error_view.py

  19.          ├── exceptions.py

  20.          ├── __init__.py

  21.          ├── logger.py

  22.          ├── response.py

  23.          ├── tools.py

  24.          └── utils.py

  25.       ├── core

  26.          ├── database.py

  27.          ├── http_handler.py

  28.          ├── http_interceptor.py

  29.          └── __init__.py

  30.       ├── extends

  31.          ├── banner.py

  32.          ├── functions.py

  33.          └── __init__.py

  34.       ├── __init__.py

  35.       ├── models

  36.          ├── base.py

  37.          ├── clazz.py

  38.          ├── __init__.py

  39.          ├── school.py

  40.          └── user.py

  41.       ├── plugins

  42.          ├── flask_celery.py

  43.          └── __init__.py

  44.       ├── services

  45.          ├── base.py

  46.          ├── __init__.py

  47.          └── statement.py

  48.       └── views

  49.           ├── async_handler.py

  50.           ├── error_handler.py

  51.           ├── index_hander.py

  52.           ├── __init__.py

  53.           ├── rest_clazz_handler.py

  54.           ├── rest_login_handler.py

  55.           ├── rest_school_handler.py

  56.           └── rest_user_handler.py

  57.    ├── git-user-config.sh

  58.    ├── README.md

  59.    ├── requirements.txt

  60.    ├── static

  61.       ├── css

  62.          └── layout.css

  63.       ├── favicon.ico

  64.       ├── images

  65.          └── 0.jpg

  66.       └── js

  67.           └── app.js

  68.    ├── stop-app.sh

  69.    ├── templates

  70.       ├── 404.html

  71.       ├── examples

  72.          ├── extensions_flask_form.html

  73.          └── extensions_flask_sqlAlchemy.html

  74.       ├── index.html

  75.       └── layout.html

  76.    └── test

  77.        ├── config.properties

  78.        ├── __init__.py

  79.        ├── plugins

  80.           ├── __init__.py

  81.           └── test_celery_task.py

  82.        ├── test_banner.py

  83.        ├── test_celery.py

  84.        ├── test_db.py

  85.        ├── test_extend_func.py

  86.        ├── test_lru.py

  87.        ├── test_platform.py

  88.        └── views

  89.            └── test_school.py

数据库封装

 
  
  1. class Database(object):

  2.    """

  3.    database interface

  4.    """

  5. class Transactional(Database):

  6.    def __init__(self, **kwargs):

  7.        """

  8.        事务层

  9.        :param auto_commit: 是否自动提交

  10.        """

  11.        self._auto_commit = kwargs.get('auto_commit', True)

  12.        self.model = kwargs.get('model_class')

  13.        if not self.model:

  14.            raise AssertionError('<class {}>: Required parameter model_class is not present.'

  15.                                 .format(self.__class__.__name__))

  16.        self.session = db.session

  17.        # logger.info('init Transactional')

  18.    def auto_commit(self):

  19.        """

  20.        是否自动提交事务

  21.        :return:

  22.        """

  23.        if self._auto_commit:

  24.            self.session.commit()

  25.    def _check_type(self, obj):

  26.        if not isinstance(obj, self.model):

  27.            raise AssertionError('obj must be <class {}> type.'

  28.                                 .format(self.model.__class__.__name__))

  29. class Persistence(Transactional):

  30.    def __init__(self, **kwargs):

  31.        super(Persistence, self).__init__(**kwargs)

  32.        # logger.info('init Persistence')

  33. class Modify(Transactional):

  34.    def __init__(self, **kwargs):

  35.        super(Modify, self).__init__(**kwargs)

  36.        # logger.info('init Modify')

  37. class Remove(Transactional):

  38.    def __init__(self, **kwargs):

  39.        super(Remove, self).__init__(**kwargs)

  40.        # logger.info('init Remove')

  41. class Query(Database):

  42.    def __init__(self, **kwargs):

  43.        # logger.info('init Query')

  44.        self.model = kwargs.get('model_class', None)

  45.        if not self.model:

  46.            raise AssertionError('<class {}>: model_class is not found.'

  47.                                 .format(self.__class__.__name__))

  48. class Modify2(Database):

  49.    @classmethod

  50.    def _auto_commit(cls):

  51.        db.session.commit()

  52. class Query2(Database):

  53.    def __init__(self):

  54.        """需要传入实体类型来使用该类"""

  55.        # logger.info('init Query2')

banner 配置

 
  
  1. def _banner():

  2.    banner_path = os.path.join(

  3.        os.path.dirname(os.path.dirname(

  4.            os.path.dirname(__file__))), 'banner.txt')

  5.    if os.path.exists(banner_path) and os.path.isfile(banner_path):

  6.        with open(banner_path) as f:

  7.            for line in f:

  8.                print line.rstrip('\n')

  9.    else:

  10.        print banner_text

640?wx_fmt=png

接口浏览

 
  
  1. $ curl localhost:5000/paths

640?wx_fmt=png

错误处理

  • 页面请求: 640?wx_fmt=png

  • curl请求:

 
  
  1. $ curl localhost:5000/api/vi/students/err

640?wx_fmt=png

级联查询转json

 
  
  1. def json(self, lazy=False, ignore=None, deep=1):

  2.    """

  3.    转化json

  4.    :param lazy: 是否懒加载

  5.    :param ignore: 过滤属性

  6.    :param deep: 当前深度

  7.    :return:

  8.    """

  9.    ignore_filed = ['query', 'metadata'] + ignore if isinstance(ignore, list) else ['query', 'metadata', ignore]

  10.    def _filter_filed(obj):

  11.        return filter(lambda y: all(

  12.            (

  13.                y not in ignore_filed,

  14.                not y.endswith('_'),

  15.                not y.startswith('_'),

  16.                not callable(getattr(obj, y))

  17.            )), dir(obj))

  18.    def _get_ignore_filed(base_obj, obj, _filed_list):

  19.        _ignore_filed = []

  20.        for _filed in _filed_list:

  21.            _filed_obj = getattr(obj, _filed)

  22.            if isinstance(_filed_obj, BaseQuery):

  23.                _primary_entity = '%s' % _filed_obj.attr.target_mapper

  24.                if _primary_entity.split('|')[1] == base_obj.__class__.__name__:

  25.                    _ignore_filed.append(_filed)

  26.            if isinstance(_filed_obj, type(base_obj)):

  27.                _ignore_filed.append(_filed)

  28.        return _ignore_filed

  29.    __relationship__, res, filed_list = None, {}, _filter_filed(self)

  30.    for filed in filed_list:

  31.        filed_type = getattr(self, filed)

  32.        if filed == __relationship__:

  33.            continue

  34.        if isinstance(filed_type, DictModel):

  35.            _ignore = _get_ignore_filed(self, filed_type, _filter_filed(filed_type))

  36.            relationship_model = filed_type.json(ignore=_ignore, deep=deep + 1)

  37.            if not lazy:

  38.                res[filed] = relationship_model

  39.        elif isinstance(filed_type, (int, list)):

  40.            res[filed] = filed_type

  41.        elif isinstance(filed_type, BaseQuery):

  42.            res[filed] = []

  43.            if not lazy:

  44.                for f in filed_type.all():

  45.                    _ignore = _get_ignore_filed(self, f, _filter_filed(f))

  46.                    res[filed].append(f.json(lazy=lazy, ignore=_ignore, deep=deep + 1))

  47.        else:

  48.            try:

  49.                if isinstance(filed_type, unicode):

  50.                    filed_type = filed_type.encode('UTF-8')

  51.                res[filed] = '{}'.format(filed_type)

  52.            except (UnicodeEncodeError, Exception), e:

  53.                logger.error('{class_name}.{filed}: {e}'.format(

  54.                    class_name=self.__class__.__name__, filed=filed, e=e))

  55.                res[filed] = None

  56.    return res

640?wx_fmt=png

拓展flask启动方法start

 
  
  1. from flaskapp import app

  2. if __name__ == "__main__":

  3.    app.start()

  4.    # app.start(port=5258, debug=False)

数据库更新迁移

 
  
  1. $ python manager.py db init

  2. $ python manager.py db migrate

Dockerfile 构建

 
  
  1. $ ./docker-build.sh

celery异步处理

  • 见项目test目录test_celery.py

 
  
  1. @celery.task()

  2. def async_compute(a, b):

  3.    from time import sleep

  4.    sleep(10)

  5.    return a + b

  6. @cly.route('/compute')

  7. @json_encoder

  8. def task():

  9.    result = async_compute.delay(1, 2)

  10.    print result.wait()

  11.    return 'task id: {}'.format(result)

swagger配置

  • 见项目examples目录swaggerforapi.py

  • 源码地址:https://github.com/tomoncle/flaskapp

640?wx_fmt=gif

Python中文社区作为一个去中心化的全球技术社区,以成为全球20万Python中文开发者的精神部落为愿景,目前覆盖各大主流媒体和协作平台,与阿里、腾讯、百度、微软、亚马逊、开源中国、CSDN等业界知名公司和技术社区建立了广泛的联系,拥有来自十多个国家和地区数万名登记会员,会员来自以公安部、工信部、清华大学、北京大学、北京邮电大学、中国人民银行、中科院、中金、华为、BAT、谷歌、微软等为代表的政府机关、科研单位、金融机构以及海内外知名公司,全平台近20万开发者关注。

640?wx_fmt=jpeg

点击阅读原文加入

Python Web开发学习小分队!

猜你喜欢

转载自blog.csdn.net/bf02jgtrs00xktcx/article/details/81040015