• flask 项目的开发经验总结


    已经开发了几个flask项目, 是时候总结一下了, 这里涉及到项目源码的组织和源码示例.

     =========================
    目录结构
    =========================    
    考虑到项目的扩展性, 采用 blueprint 进行组织. 假设 flaskapp 为根目录, 主要的程序放在 app 包中, 除了后台代码, 在app目录下还有templates/static/子目录. 为了重用, 最好的形式是一个建立 boilerplate 项目.
    主要参考文档为:
    https://github.com/mitsuhiko/flask/wiki/Large-app-how-to
    http://www.realpython.com/blog/python/python-web-applications-with-flask-part-ii-app-creation
    http://www.realpython.com/blog/python/rethink-flask-a-simple-todo-list-powered-by-flask-and-rethinkdb/

            
    flaskapp
    ├── app
    │   ├── __init__.py
    │   ├── constants.py
    │   ├── users[sub_app/module]
    │   │   ├── constants.py
    │   │   ├── decorators.py
    │   │   ├── forms.py
    │   │   ├── models.py
    │   │   └── views.py
    │   ├── tickets[sub_app/module]
    │   │   ├── constants.py
    │   │   ├── decorators.py
    │   │   ├── forms.py
    │   │   ├── models.py
    │   │   └── views.py
    │   ├── templates
    │   │   ├── forms
    │   │   │   └── macros.html
    │   │   ├── base.html      
    │   │   ├── index.html  
    │   │   ├── base_another.html  
    │   │   ├── 500.html (server error page)
    │   │   ├── 404.html (not found page)
    │   │   ├── method_not_allowed.html
    │   │   ├── access_forbidden.html
    │   │   ├── users
    │   │   │   ├── profile.html  
    │   │   │   ├── login.html          
    │   │   │   └── register.html
    │   │   └── tickets
    │   │        ├── create.html          
    │   │        └── close.html                                
    │   └── static
    │        ├── favicon.ico
    │        ├── img
    │        ├── js
    │        │   ├── main.js #our own js code
    │        │   └── vendor
    │        │        ├── bootstrap.min.js
    │        │        └── jquery-1.7.2.min.js
    │        └── css
    │             ├── layout.less
    │             ├── reset.less
    │             └── vendor
    │                  └── bootstrap.css  
    ├── flaskapp.db
    ├── config.py
    ├── requirements.txt
    ├── runserver.py
    ├── shell.py     
    ├── tests 目录
    └── docs 目录


    ----------------------
    项目级的单元
    ----------------------
    runserver.py 用来启动 web server,  从app包中进入flask app对象, 然后直接启动.
          
     
    config.py 存储一些db的 connection 配置, 以及Flask SECRET_KEY 等等. 更多的配置项见 http://flask.pocoo.org/docs/config/
        
       
    ----------------------
    应用级别的单元  
    ----------------------
    flaskapp/app/__init__.py, 作为整个app的入口, 做如下工作.
        1.加载flask的config,
        2.[如使用Flask-SQLAlchemy插件]创建 SqlAlchemy 的db 实例.
        3.[如没使用Flask-SQLAlchemy插件]定义3个函数, 分别加上@app.before_request和@app.teardown_request和@app.after_request.  before_request和teardown_request函数非常适合做创建和关闭db connection工作. after_request函数不适合用来关闭db connection, 因为after_request函数在有unhandled exception发生的情况下, 会被跳过.  而teardown 函数总是能保证被调用的.
        4.注册sub app blueprint, 比如users和tickets
        5.设置root url 和favicon.ico 的路由,  
        6.创建login_manager, 比如使用flask-login插件来创建一个login_manager
        


    ----------------------
    子应用级别的单元,
    ----------------------    
    称为subapp或称为module, 目录采用复数形式
    flaskapp/app/users/models.py, 和User相关的表模型
    flaskapp/app/users/constants.py, 和User module相关的constant, 比如用户的激活状态, 用户的类型.
    flaskapp/app/users/forms.py, 集中所有User module相关的表单类, 比如class LoginForm(Form) 和 class RegisterForm(Form) 类.
    flaskapp/app/users/decorators.py, 和User module相关的一些decorator, 比如 requires_login, 供 views.py 使用.
    flaskapp/app/users/views.py, 充当url路由角色(Flask是基于MVT模型, 这里的view相当于MVC模型中的Controller). 依据web请求类型和请求的url, 路由到指定的view函数, 在view函数中, 做逻辑处理, 然后, 或展现form, 或跳转到其他url.
            
     
    ----------------------
    templates目录
    ----------------------
    flaskapp/app/templates/base.html, 模板的模板, 根据需要, 可以设置多个base页面
    flaskapp/app/templates/forms/macros.html, 定义一些宏, 供form页面调用, 用来渲染form的元素
    flaskapp/app/templates/users/login.html, 在flaskapp/app/users/views.py应该有一个同名的view函数



    ----------------------
    static目录
    ----------------------
    flaskapp/app/static/favicon.ico, 16 × 16 pixels and in the ICO format


     

    =========================
    源码示例
    =========================

    ------------------------------
    runserver.py
    ------------------------------
    runserver.py 用来启动 web server.
    # -*- coding: utf-8 -*-  
    from __future__ import absolute_import from app import app
    
    app.run()

        
    ------------------------------
    config.py
    ------------------------------
    config.py 存储一些db的 connection 配置, 以及Flask SECRET_KEY 等等. 更多的配置项见 http://flask.pocoo.org/docs/config/
    # -*- coding: utf-8 -*-  
    from __future__ import absolute_import
    import os
    from datetime import timedelta
    _basedir = os.path.abspath(os.path.dirname(__file__))
    
    DEBUG = True
    
    SECRET_KEY = os.urandom(24)
    PERMANENT_SESSION_LIFETIME=timedelta(seconds=24*60*60)
    
    CSRF_ENABLED = True
    CSRF_SESSION_KEY = SECRET_KEY  
      
       
    ------------------------------
    app/__init__.py
    ------------------------------   
    flaskapp/app/__init__.py, 作为整个app的入口, 做如下工作.
        1.加载flask的config,
        2.[如使用Flask-SQLAlchemy插件]创建 SqlAlchemy 的db 实例.
        3.[如没使用Flask-SQLAlchemy插件]定义3个函数, 分别加上@app.before_request和@app.teardown_request和@app.after_request.  before_request和teardown_request函数非常适合做创建和关闭db connection工作. after_request函数不适合用来关闭db connection, 因为after_request函数在有unhandled exception发生的情况下, 会被跳过.  而teardown 函数总是能保证被调用的.
        4.注册sub app blueprint, 比如users和tickets
        5.设置root url 和favicon.ico 的路由,  
        6.创建login_manager, 比如使用flask-login插件来创建一个login_manager

    # -*- coding: utf-8 -*-  
    from __future__ import absolute_import
    from flask import Flask, g, render_template, send_from_directory
    import os
    import os.path
    _basedir = os.path.abspath(os.path.dirname(__file__))
    configPy=os.path.join(os.path.join( _basedir,os.path.pardir), 'config.py')
    
    app = Flask(__name__)  # create our application object
    
    app.config.from_pyfile(configPy)
    #app.debug=True  #change some attribute after load configuration
    
    
    flask_sqlalchemy_used=True  # 如果使用Flask-SQLAlchemy了
    db = SQLAlchemy(app) #create a db (SQLAlchemy) object from our app object
    
    login_manager = LoginManager(app) #create a LoginManager Object from our app object
    #add our view as the login view to finish configuring the LoginManager
    login_manager.login_view = "users.login_view"
    
    #register the users module blueprint
    from app.users.views import mod as usersModule
    app.register_blueprint(usersModule)
    
    
    #register the tickets module blueprint
    from app.tickets.views import mod as ticketsModule
    app.register_blueprint(ticketsModule)
    
    
    def connect_db():  # 如果没使用Flask-SQLAlchemy
        if not flask_sqlalchemy_used:
            return sqlite3.connect('/path/to/database.db')
        else:
            return None
        
                    
    @app.before_request
    def before_request():
        """Make sure we are connected to the database each request."""
        if not flask_sqlalchemy_used:
            g.db = connect_db()
    
    
    @app.teardown_request
    def teardown_request(response):
        """Closes the database again at the end of the request."""
        if not flask_sqlalchemy_used:
            g.db.close()
        return response
        
                
    #*****************
    # controllers
    #*****************
    
    @app.route('/favicon.ico')
    def favicon():
        return send_from_directory(os.path.join(app.root_path, 'static'), 'ico/favicon.ico')
    
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'), 404
    
    @app.route("/")
    def index():
        return render_template('index.html')

    ------------------------------
    app/users/views.py.py
    ------------------------------
    # -*- coding: utf-8 -*-  
    from __future__ import absolute_import
    from flask import Blueprint, render_template, flash, redirect, session, url_for, request, g
    from flask.ext.login import login_user, logout_user, login_required
    from app import app, db, login_manager
    from forms import LoginForm, RegistrationForm
    from app.users.models import User
    
    mod = Blueprint('users', __name__) #register the users blueprint module
    
    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(user_id)
    
    @mod.route('/login/', methods=('GET', 'POST'))
    def login_view():
        form = LoginForm(request.form)
        if form.validate_on_submit():
            user = form.get_user()
            login_user(user)
            flash("Logged in successfully.")
            return redirect(request.args.get("next") or url_for("index"))
        return render_template('users/login.html', form=form)
    
    @mod.route('/register/', methods=('GET', 'POST'))
    def register_view():
        form = RegistrationForm(request.form)
        if form.validate_on_submit():
            user = User()
            form.populate_obj(user)
            db.session.add(user)   #使用SqlAlchemy保存
            db.session.commit()
            login_user(user)
            return redirect(url_for('index'))
        return render_template('users/register.html', form=form)
    
    @login_required
    @mod.route('/logout/')
    def logout_view():
        logout_user()
        return redirect(url_for('index'))
    macros.html, 是个jinja2的宏文件, 我们在该文件中可定义一些宏, 供form页面调用, 用来渲染form的元素. 用法是, 在我们的html文件中, 引入这个宏文件即可.
     
    用法:
    {% from "macros.html" import form_field %}

    macros.html内容, 可以自动将form兼容 bootstrap. 内容摘自 https://gist.github.com/rawrgulmuffins/6025599   
    {% macro form_field(field) -%}
        {% set with_label = kwargs.pop('with_label', False) %}
        {% set placeholder = '' %}
        {% if not with_label %}
            {% set placeholder = field.label.text %}
        {% endif %}
        <div class="control-group {% if field.errors %}error{% endif %}">
            {% if with_label %}
                <label for="{{ field.id }}" class="control-label">
                    {{ field.label.text }}{% if field.flags.required %} *{% endif %}:
                </label>
            {% endif %}
            <div class="controls">
                {% set class_ = kwargs.pop('class_', '') %}
                {% if field.flags.required %}
                    {% set class_ = class_ + ' required' %}
                {% endif %}
                {% if field.type == 'BooleanField' %}
                    <label class="checkbox">
                        {{ field(class_=class_, **kwargs) }}
                        {{ field.label.text|safe }}
                    </label>
                {% else %}
                    {% if field.type in ('TextField', 'TextAreaField', 'PasswordField') %}
                        {% set class_ = class_ + ' input-xlarge' %}
                    {% elif field.type == 'FileField' %}
                        {% set class_ = class_ + ' input-file' %}
                    {% endif %}
    
                    {% if field.type == 'SelectField' %}
                      {{ field(class_=class_, **kwargs) }}
                    {% else %}
                      {{ field(class_=class_, placeholder=placeholder, **kwargs) }}
                    {% endif %}
    
                {% endif %}
                {% if field.errors %}
                    <span class="error help-inline">{{ field.errors|join(', ') }}</span>
                {% endif %}
                {% if field.description %}
                    <p class="help-block">{{ field.description|safe }}</p>
                {% endif %}
            </div>
        </div>
    {%- endmacro %} 
     
         
  • 相关阅读:
    Json -- 语法和示例,javascript 解析Json
    平衡二叉树的实现原理
    递归:汉诺塔
    递归:这帮坑爹的小兔崽子
    函数:递归是神马
    函数:lambda表达式
    函数:内嵌函数和闭包
    函数:我的地盘听我的
    函数:灵活即强大
    函数:Python的乐高积木
  • 原文地址:https://www.cnblogs.com/harrychinese/p/flask_project_right_way.html
Copyright © 2020-2023  润新知