Flask备注4(Structure)
Flask备注4(Structure)
package
通过Flask可以非常简单的通过一个module(一个py文件)创建一个简单的application。这种简单程序的文件结构如下:
/yourapplication
/yourapplication.py
/static
/style.css
/templates
layout.html
index.html
login.html
...
这种结构对于较大或者复杂的程序并不合适。对于复杂程序可以通过python自带的package结构来组织代码结构。
包含init.py的文件夹都是package,引用该目录下的所有module都会先导入init.py并执行顶层代码。
使用package的Flask的代码结构如下:
/yourapplication
/runserver.py
/yourapplication
/__init__.py
/application.py
/views.py
/static
/style.css
/templates
layout.html
index.html
login.html
...
因为导入任何目录下的文件,都会执行__init__.py中的顶层代码,而且在多层嵌套的情况下,引用会执行每层目录的__init__.py文件,因此建议将次文件留空。将单独module的程序转变为package结构程序的步骤如下:
- 将App的初始化以及参数的配置等功能放到application.py中。
- 在每个单独的功能module(例如views.py)通过
from application import app对app进行引用,以使用app的接口。 - 创建一个顶层module(runserver.py)来执行application,在此module中先引入app初始module
import yourapplication.application然后分别导入功能module例如import yourapplication.views,最终执行applicationapp.run(debug=True)。
在代码里面应该尽量避免循环引用,避免依赖循环。在上述步骤里面。modulerunserver依赖application以及各个功能module,各个功能module依赖于applicationmodule,形成不了依赖循环。当不得不面对循环依赖时,将其中的一个应用放到函数或者方法里面。
Blueprints
Flask支持blueprint将application可以分成几个部分,从接口功能上,blueprint对象和flask对象的类似。在Flask application中增加blueprint的支持,可以为较大或者复杂的application提供了一个新的组织结构方式:将程序中相似的部分功能放到一个blueprint对象中,然后将这些blueprints注册成到application。最终的application包含一个application对象(flask对象),所需要的extension的对象以及一系列blueprint对象。使用这种结构的优势:
- 将一个较大或者复杂的appliction转变为一系列相对独立的blueprints,便于维护。
- 每个blueprint在注册时对应一个URL前缀和subdomain,这样所有包含于blueprints中的view函数都以此前缀和subdomain作为参数。
- 可以将同一个blueprints,使用不同的URL规则进行注册。实现模块化复用代码。
- 对blueprints可以单独提供template filter,templates文件目录以及static文件目录。优先级比application的templates和static的优先级要低一些。
在使用blueprint的组织结构中,每个blueprint部分必须包含一个blueprint对象,以及这个部分的功能实现。blueprint对象的声明示例如下:
from flask import Blueprint
bp = Blueprint('blueprint_user', __name__, template_folder='templates')
每个blueprint部分的实现简单点可以放在一个module中同样也可以和package结构相结合,放到一个目录结构中。和package结构结合的代码结构如下:
/yourapplication
/runserver.py
/yourapplication
/__init__.py
/application.py
/views.py
/static
/style.css
/templates
layout.html
index.html
login.html
/bpuser
/__init__.py
/blueprint_user.py
/views.py
/templates
info.html
/bpmanager
/__init__.py
/blueprint_manager.py
...
在示例代码中,bpuser以及bpmanager是两个blueprints的目录。其中在blueprint_user.py以及blueprint_manager.py进行了blueprint对象的声明。在application.py中引用并注册blueprints。注册时示例如下:
from flask import Flask
import bpuser.blueprint_user
import bpmanager.blueprint_manager
app = Flask(__name__)
app.register_blueprint(blueprint_user.bp, url_prefix='/user')
app.register_blueprint(blueprint_manager.bp, url_prefix='manager')
在application注册blueprint时,从根本实现上,application会记录blueprint的功能。然后application在功能触发时根据记录分发到相应的blueprint所在的模块进行处理。例如在blueprint_user.py中声明一个view endpoint。
@blueprint_user.route('/info')
def info():
try:
return render_template('info.html')
except TemplateNotFound:
abort(404)
然后application在注册blueprint时,将blueprint的功能记录在application中,在application中增加一些规则,在application使用时会根据相应规则发送到blueprint进行处理。这些记录的规则示例如下:
[<Rule '/user/info' (HEAD, OPTIONS, GET) ->blueprint_user.info>]
如规则中所示,在application使用blueprint所声明的endpoint(入口函数)都加了一个前缀,这个前缀就是blueprint的名字。因此在进行url转换时,使用url_for函数也必须要在endpoint前加上blueprint名字的前缀。如果转换函数在当前blueprint中使用,可以只在endpoint加一个点。
# in application
url_for(blueprint_user.info)
# in blueprint
url_for(.info)
面向对象编程
如我们所知,面向对象的编程思想以及设计模式能够提供更好的代码结构,当前所描述的基于blueprint以及package的代码结构,虽然代码的实现在Module(源文件)以及函数中,但是依然符合了OOP(面向对象编程)的思想,同样也使用了相应的设计模式。也可以理解为当前结构是面向对象的结构。
- everthing in python is object. python中的一切都是对象,也可以按照处理对象的方式处理。比如函数、module、字符串、以及上述的package和blueprint都是对象。因此可以获取类型,可以作为参数传入函数,作为函数值,甚至包含属性和方法。因此这个结构是面向对象的。
- package以及blueprint的结构是使用目的,就是封装以及多态。这本身就符合面向对象的设计思想。blueprint的注册,decorator的使用本身也是设计模式的应用。因此这个结构是面向对象的。
但是当前结构中没有使用类(class),python的代码设计并不需要类来完成面向对象的设计,灵活的Module以及对于环境影响较小的函数是更推荐的方式。因此使用类需要基于以下原则:
- 如果需要将功能以及功能的状态绑定在一起,可以通过自定义类实现。将功能变为方法(method)将状态变为属性(property)。
- 如果功能会被多个线程使用,他所操作的资源在多线程环境下具备异步风险,因此不建议使用自定义类。
对于Flask程序,业务逻辑的实现,因为会同时发生很多个相同的请求,因此并不建议放到自定义类中。而用户界面在会有较多的功能重用,并且单一View会响应同一入口的不同的Http方法,因此可以放到自定义类中。对于用户界面的自定义类,Flask引入Pluggable view。
Pluggable View
通过Pluggable view,Flask通过自定义类为URL入口提供View。这种方式相比较于函数endpoint结构更清晰,同时提供了更多的灵活性。通过自定义类提供endpoint的简单示例如下:
from flask import View, render_template
class ShowUsersView:
def dispatch_request(self):
users = User.query.all()
return render_tempalte('show_users.html', objects = users)
app.add_url_rule('/users/', view_func=ShowUsersView.as_view('show_users'))
自定义类提供VIEW的实现的必要因素有:
- 自定义类必须继承自View类(或者MethodView类)。
- 实现dispatch_request函数。
- 在app添加url处理规则时,通过as_view方法转变为一个endponit函数。其中参数就是endponit名称。
相比较于endpoint函数的方式,Pluggable view实现方式的优势包含:
- 通过一个类,对应同一入口可以定义不同的方法应对不同的http方法,结构更清晰。
- 通过类的继承和多态的特点将相似度很高的VIEW整合在一起,代码复用度较高切结构清晰。
Method View
自定义View继承MehtodView,可以在自定义View中响应不同的Htpp方法的请求。例如上述'/users/'的URL中可以对应的方法包含:
| *URL* | *Method* | *Description* |
| /users/ | GET | 获取所有的用户列表 |
| /users/ | POST | 创建新用户 |
| /users/id | GET | 获取单个用户 |
| /users/id | PUT | 更新单个用户 |
| /users/id | DELETE | 删除单个用户 |
通过继承自MethodView的自定义View,这些HTTP请求都可以在一个入口清晰的实现。实现示例:
class UsersView(MethodView):
def get(self, user_id):
if user_id is None:
# return all users.
pass
else:
# return a singal view
pass
def post(self):
# create a new user
pass
def put(self, user_id):
# update a user
pass
def delete(self, user_id):
# delete a user.
pass
然后逐条注册URL处理规则,同时可以将整个注册抽象出来作为函数在所有的自定义VIEW注册时使用。
def register_api(view, endpoint, url, pk='id', pk_type='int'):
view_func = view.as_view(endpoint)
app.add_url_rule(url, defaults={pk: None},
view_func=view_func, methods=['GET',])
app.add_url_rule(url,
view_func=view_func, methods=['POST',])
app.add_url_rule('%s<%s:%s> % (url, pk, pk_type)
view_func=view_func,
methods=['GET', 'PUT', 'DELETE'])
register_api(UsersView, 'users', '/users/', pk='user_id', pk_type='int')
View inherits
Pluggable view是自定义类,因此可以使用类的继承和多态的特性,将相似的VIEW整合在一起。例如:Users的VIEW是一个列表; Items的VIEW也是一个列表;因此可以继承自一个自定义的ListView。VIEW的继承关系可以和对应templates的继承关系一致,也也可以都使用父VIEW的template,非常灵活。
更进一步使用自定义类定义VIEW,可以将VIEW的实现已经VIEW的注册分发这种和业务逻辑有关的部分分离开来,将程序变成MVC的机构:
VIEW <--> Model <--> 业务
当前使用这种结构需要符合应用的真实需要。代码结构设计的目的是更清晰的代码结构,更容易的代码阅读和维护。
Flask备注4(Structure)的更多相关文章
- Flask备注三(Context)
Flask备注三(Context) Flask支持不同的应用场景下,对应不同的local context(本地上下文环境),用来提供当前环境下的资源.lcoal context和全局变量以及局部变量最 ...
- Flask备注二(Configurations, Signals)
Flask备注二(Configuration, Signals) Flask是一个使用python开发Web程序的框架.依赖于Werkzeug提供完整的WSGI支持,以及Jinja2提供templat ...
- Flask 备注一(单元测试,Debugger, Logger)
Flask 备注一(单元测试,Debugger, Logger) Flask是一个使用python开发Web程序的框架.依赖于Werkzeug提供完整的WSGI支持,以及Jinja2提供templat ...
- flask-admin章节四:flask session的使用
1. 关于session flask session可能很多人根本都没有使用过,倒是cookie大家可能使用得比较多.flask cookie使用起来比较简单,就两个函数,读取和设置. 具体使用方式如 ...
- python flask detect browser language
python flask detect browser language No problem. We won't show you that ad again. Why didn't you l ...
- Flask and uWSGI - unable to load app 0 (mountpoint='') (callable not found or import error)
Here is my directory structure: -/path/to/folder/run.py -|app -|__init__.py -|views.py -|templates - ...
- flask程序部署在openshift上的一些注意事项
https://www.openshift.com/blogs/how-to-install-and-configure-a-python-flask-dev-environment-deploy-t ...
- flask 动手写的接口平台
笔者做的是测试,在群里经常有人讨论,怎么和开发对接怎么难,怎么测接口比较难,开发不愿因写文档等等,是啊,我感觉也是这样,沟通,还有我们应该怎样去学习,去扩充自己,让自己不再受开发所左右, 笔者就像试图 ...
- 基于Python的Flask的开发实战(第二节程序的基本结构)
1.初始化 所有的flask程序都必须创建一个程序实例 web服务器使用wsgi接口协议,把接收客户端的请求都转发给这个程序实例来进行处理.这个程序实例就是flask对象 from flask imp ...
随机推荐
- wc命令
wc命令的功能为统计指定文件中的字节数.字数.行数, 并将统计结果显示输出. 语法:wc [选项] 文件- 说明:该命令统计给定文件中的字节数.字数.行数.如果没有给出文件名,则从标准输入读取.wc同 ...
- 图解GitHub基本操作
目录 一.注册并登陆到github网站 1.1.打开github网站首页(https://github.com/) 1.2.注册一个自己的github账号 1.3.登陆自己的github账号 二.创建 ...
- target,currentTarget和this三者的区别
target在事件流的目标阶段:currentTarget在事件流的捕获,目标及冒泡阶段.只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的 ...
- 第六百零九天 how can I 坚持
好失败啊,搞了一天,竟然连个环境都没搞好,也是醉了.还是太渣了. 洗澡,睡觉.
- C#的自定义滚动条
VS工具箱自带的滚动条,不能设置颜色. 在网上找资源,找到一个控制TextBox的垂直滚动条,链接为http://www.cnblogs.com/2seek/p/4455079.html 在这个的基础 ...
- poi API
一. POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 二. HSSF概况 HSSF 是 ...
- iOS强制屏幕旋转
/** 强制旋转屏幕为纵向 (注:这种方式 键盘不能旋转过来; iOS8.x下 UIAlterView旋转不过来 ) @return */ + (void)rotateOrientationPort ...
- HBase预分区
seq 0 7 | awk '{printf("\\x%02x\\x%02x\n", $1/256, $1%256);}' | sort -R |head -3 create 'm ...
- Angular JS中双击事件ng-dblclick避免同时触发两次单击事件ng-click的解决方案
有些需求中,需要一个元素上既有双击事件,也有单击事件,而两者实现的效果不一样. 这时可以使用ng-dblclick与ng-click来实现需求,但是要避免浏览器将双击事件误认为是两次单击事件,从而出现 ...
- CListBox多选情况处理方法
如ListBox的内容如下,蓝色代表选中的内容 列表 索引 删除时索引 item1 0 0 item2 1 0 item3 2 item4 3 1 删除所有选中列: vo ...