Flask 项目的开发经验总结

 原文链接:http://www.cnblogs.com/harrychinese/p/flask_project_right_way.html

 

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

=========================
需要的 python 包有:
=========================
#basic packages
    Werkzeug
    itsdangerous
    Jinja2   
    Flask
    SQLAlchemy 
    argparse
    MarkupSafe, Implements a XML/HTML/XHTML Markup safe string for Python
    
#Flask 最有用的插件
    flask-admin, 快速搭建CRUD功能
    Flask-Cache, 将@cache(timeout=300)放在@route(url)后面, 既可以cache了.
    flask-login, user登录session方面的管理
    Flask-WTF
    flask-debugtoolbar, http://flask-debugtoolbar.readthedocs.org/en/latest/, 会自动在页面右侧加一个debug toolbar
    
#Flask 其他插件 
    Flask-RESTful, github.com/twilio/flask-restful, 构建restful api, 并以class的形式组织url routing.      
    Flask-Babel, 本地化和国际化(i18n)
    Flask-Security, 包括用户注册,密码hash, 忘记密码功能       

=========================
目录结构
=========================    
考虑到项目的扩展性, 采用 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 %} 
复制代码
 

 

转载自演道,想查看更及时的互联网产品技术热点文章请点击http://go2live.cn