flask 项目搭建及配置分享

112 阅读4分钟

****

作者: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.    .
  4.    ├── banner.txt
  5.    ├── bootstrap_app.py
  6.    ├── bootstrap_celery.py
  7.    ├── config.properties
  8.    ├── config.py
  9.    ├── Dockerfile
  10.    ├── examples
  11.    │   ├── extensions_flask_form.py
  12.    │   ├── extensions_flask_SQLAlchemy.py
  13.    │   ├── hello_world.py
  14.    │   ├── index.py
  15.    │   ├── __init__.py
  16.    │   └── rest_api.py
  17.    ├── flaskapp
  18.    │   ├── common
  19.    │   │   ├── error_view.py
  20.    │   │   ├── exceptions.py
  21.    │   │   ├── __init__.py
  22.    │   │   ├── logger.py
  23.    │   │   ├── response.py
  24.    │   │   ├── tools.py
  25.    │   │   └── utils.py
  26.    │   ├── core
  27.    │   │   ├── database.py
  28.    │   │   ├── http_handler.py
  29.    │   │   ├── http_interceptor.py
  30.    │   │   └── __init__.py
  31.    │   ├── extends
  32.    │   │   ├── banner.py
  33.    │   │   ├── functions.py
  34.    │   │   └── __init__.py
  35.    │   ├── __init__.py
  36.    │   ├── models
  37.    │   │   ├── base.py
  38.    │   │   ├── clazz.py
  39.    │   │   ├── __init__.py
  40.    │   │   ├── school.py
  41.    │   │   └── user.py
  42.    │   ├── plugins
  43.    │   │   ├── flask_celery.py
  44.    │   │   └── __init__.py
  45.    │   ├── services
  46.    │   │   ├── base.py
  47.    │   │   ├── __init__.py
  48.    │   │   └── statement.py
  49.    │   └── views
  50.    │       ├── async_handler.py
  51.    │       ├── error_handler.py
  52.    │       ├── index_hander.py
  53.    │       ├── __init__.py
  54.    │       ├── rest_clazz_handler.py
  55.    │       ├── rest_login_handler.py
  56.    │       ├── rest_school_handler.py
  57.    │       └── rest_user_handler.py
  58.    ├── git-user-config.sh
  59.    ├── README.md
  60.    ├── requirements.txt
  61.    ├── static
  62.    │   ├── css
  63.    │   │   └── layout.css
  64.    │   ├── favicon.ico
  65.    │   ├── images
  66.    │   │   └── 0.jpg
  67.    │   └── js
  68.    │       └── app.js
  69.    ├── stop-app.sh
  70.    ├── templates
  71.    │   ├── 404.html
  72.    │   ├── examples
  73.    │   │   ├── extensions_flask_form.html
  74.    │   │   └── extensions_flask_sqlAlchemy.html
  75.    │   ├── index.html
  76.    │   └── layout.html
  77.    └── test
  78.        ├── config.properties
  79.        ├── __init__.py
  80.        ├── plugins
  81.        │   ├── __init__.py
  82.        │   └── test_celery_task.py
  83.        ├── test_banner.py
  84.        ├── test_celery.py
  85.        ├── test_db.py
  86.        ├── test_extend_func.py
  87.        ├── test_lru.py
  88.        ├── test_platform.py
  89.        └── views
  90.            └── test_school.py

数据库封装

  1. class Database(object):
  2.    """
  3.    database interface
  4.    """
  5. ``
  6. ``
  7. class Transactional(Database):
  8.    def __init__(self, **kwargs):
  9.        """
  10.        事务层
  11.        :param auto_commit: 是否自动提交
  12.        """
  13.        self._auto_commit = kwargs.get('auto_commit', True)
  14.        self.model = kwargs.get('model_class')
  15.        if not self.model:
  16.            raise AssertionError('<class {}>: Required parameter model_class is not present.'
  17.                                 .format(self.__class__.__name__))
  18.        self.session = db.session
  19.        # logger.info('init Transactional')
  20. ``
  21.    def auto_commit(self):
  22.        """
  23.        是否自动提交事务
  24.        :return:
  25.        """
  26.        if self._auto_commit:
  27.            self.session.commit()
  28. ``
  29.    def _check_type(self, obj):
  30.        if not isinstance(obj, self.model):
  31.            raise AssertionError('obj must be <class {}> type.'
  32.                                 .format(self.model.__class__.__name__))
  33. ``
  34. ``
  35. class Persistence(Transactional):
  36.    def __init__(self, **kwargs):
  37.        super(Persistence, self).__init__(**kwargs)
  38.        # logger.info('init Persistence')
  39. ``
  40. ``
  41. class Modify(Transactional):
  42.    def __init__(self, **kwargs):
  43.        super(Modify, self).__init__(**kwargs)
  44.        # logger.info('init Modify')
  45. ``
  46. ``
  47. class Remove(Transactional):
  48.    def __init__(self, **kwargs):
  49.        super(Remove, self).__init__(**kwargs)
  50.        # logger.info('init Remove')
  51. ``
  52. ``
  53. class Query(Database):
  54.    def __init__(self, **kwargs):
  55.        # logger.info('init Query')
  56.        self.model = kwargs.get('model_class', None)
  57.        if not self.model:
  58.            raise AssertionError('<class {}>: model_class is not found.'
  59.                                 .format(self.__class__.__name__))
  60. ``
  61. ``
  62. class Modify2(Database):
  63.    @classmethod
  64.    def _auto_commit(cls):
  65.        db.session.commit()
  66. ``
  67. ``
  68. class Query2(Database):
  69.    def __init__(self):
  70.        """需要传入实体类型来使用该类"""
  71.        # 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. ``
  11.    def _filter_filed(obj):
  12.        return filter(lambda y: all(
  13.            (
  14.                y not in ignore_filed,
  15.                not y.endswith('_'),
  16.                not y.startswith('_'),
  17.                not callable(getattr(obj, y))
  18.            )), dir(obj))
  19. ``
  20.    def _get_ignore_filed(base_obj, obj, _filed_list):
  21.        _ignore_filed = []
  22.        for _filed in _filed_list:
  23.            _filed_obj = getattr(obj, _filed)
  24.            if isinstance(_filed_obj, BaseQuery):
  25.                _primary_entity = '%s' % _filed_obj.attr.target_mapper
  26.                if _primary_entity.split('|')[1] == base_obj.__class__.__name__:
  27.                    _ignore_filed.append(_filed)
  28.            if isinstance(_filed_obj, type(base_obj)):
  29.                _ignore_filed.append(_filed)
  30.        return _ignore_filed
  31. ``
  32.    __relationship__, res, filed_list = None, {}, _filter_filed(self)
  33.    for filed in filed_list:
  34.        filed_type = getattr(self, filed)
  35.        if filed == __relationship__:
  36.            continue
  37.        if isinstance(filed_type, DictModel):
  38.            _ignore = _get_ignore_filed(self, filed_type, _filter_filed(filed_type))
  39.            relationship_model = filed_type.json(ignore=_ignore, deep=deep + 1)
  40.            if not lazy:
  41.                res[filed] = relationship_model
  42.        elif isinstance(filed_type, (int, list)):
  43.            res[filed] = filed_type
  44.        elif isinstance(filed_type, BaseQuery):
  45.            res[filed] = []
  46.            if not lazy:
  47.                for f in filed_type.all():
  48.                    _ignore = _get_ignore_filed(self, f, _filter_filed(f))
  49.                    res[filed].append(f.json(lazy=lazy, ignore=_ignore, deep=deep + 1))
  50.        else:
  51.            try:
  52.                if isinstance(filed_type, unicode):
  53.                    filed_type = filed_type.encode('UTF-8')
  54.                res[filed] = '{}'.format(filed_type)
  55.            except (UnicodeEncodeError, Exception), e:
  56.                logger.error('{class_name}.{filed}: {e}'.format(
  57.                    class_name=self.__class__.__name__, filed=filed, e=e))
  58.                res[filed] = None
  59.    return res

640?wx_fmt=png

拓展flask启动方法start

  1. from flaskapp import app
  2. ``
  3. if __name__ == "__main__":
  4.    app.start()
  5.    # 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. ``
  7. ``
  8. @cly.route('/compute')
  9. @json_encoder
  10. def task():
  11.    result = async_compute.delay(1, 2)
  12.    print result.wait()
  13.    return 'task id: {}'.format(result)

swagger配置

640?wx_fmt=gif

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

640?wx_fmt=jpeg

点击阅读原文加入

Python Web开发****学习小分队!