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结构程序的步骤如下:

  1. 将App的初始化以及参数的配置等功能放到application.py中。
  2. 在每个单独的功能module(例如views.py)通过from application import app 对app进行引用,以使用app的接口。
  3. 创建一个顶层module(runserver.py)来执行application,在此module中先引入app初始moduleimport 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(面向对象编程)的思想,同样也使用了相应的设计模式。也可以理解为当前结构是面向对象的结构。

  1. everthing in python is object. python中的一切都是对象,也可以按照处理对象的方式处理。比如函数、module、字符串、以及上述的package和blueprint都是对象。因此可以获取类型,可以作为参数传入函数,作为函数值,甚至包含属性和方法。因此这个结构是面向对象的。
  2. 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实现方式的优势包含:

  1. 通过一个类,对应同一入口可以定义不同的方法应对不同的http方法,结构更清晰。
  2. 通过类的继承和多态的特点将相似度很高的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)的更多相关文章

  1. Flask备注三(Context)

    Flask备注三(Context) Flask支持不同的应用场景下,对应不同的local context(本地上下文环境),用来提供当前环境下的资源.lcoal context和全局变量以及局部变量最 ...

  2. Flask备注二(Configurations, Signals)

    Flask备注二(Configuration, Signals) Flask是一个使用python开发Web程序的框架.依赖于Werkzeug提供完整的WSGI支持,以及Jinja2提供templat ...

  3. Flask 备注一(单元测试,Debugger, Logger)

    Flask 备注一(单元测试,Debugger, Logger) Flask是一个使用python开发Web程序的框架.依赖于Werkzeug提供完整的WSGI支持,以及Jinja2提供templat ...

  4. flask-admin章节四:flask session的使用

    1. 关于session flask session可能很多人根本都没有使用过,倒是cookie大家可能使用得比较多.flask cookie使用起来比较简单,就两个函数,读取和设置. 具体使用方式如 ...

  5. 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 ...

  6. 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 - ...

  7. flask程序部署在openshift上的一些注意事项

    https://www.openshift.com/blogs/how-to-install-and-configure-a-python-flask-dev-environment-deploy-t ...

  8. flask 动手写的接口平台

    笔者做的是测试,在群里经常有人讨论,怎么和开发对接怎么难,怎么测接口比较难,开发不愿因写文档等等,是啊,我感觉也是这样,沟通,还有我们应该怎样去学习,去扩充自己,让自己不再受开发所左右, 笔者就像试图 ...

  9. 基于Python的Flask的开发实战(第二节程序的基本结构)

    1.初始化 所有的flask程序都必须创建一个程序实例 web服务器使用wsgi接口协议,把接收客户端的请求都转发给这个程序实例来进行处理.这个程序实例就是flask对象 from flask imp ...

随机推荐

  1. unity3d中检测一个物体是否在摄像机视野范围内

    这个脚本最好是把模型对象的锚点设置在最低点.好了直接上脚本.可以直接复制代码,把CS文件拖到一个Camera上,然后把目标拖到targetTran中去就行了. using UnityEngine; u ...

  2. 【c++】虚基类

    何要使用虚基类: 为何避免多层继承中出项多个公共基类所造成的歧义现象 虚基类用法 派生类继承基类时,加上一个virtual关键词则为虚拟基类继承. 在上图程序运行中,我们发现class bass的构造 ...

  3. apache日志轮询技术

    1.首先先下载安装apache的日志轮询工具cronolog: wget http://cronolog.org/download/cronolog-1.6.2.tar.gz .tar.gz cd c ...

  4. HPUX 只取syslog.log当前三天的信息

    LOG_DAYS=1todays_date=`date +%m:%d:%Y`current_date=`echo $todays_date | sed 's/://g'`day=`echo $toda ...

  5. jquery change dropdownlist selected option

    <select name="corporation"> <option value="1">corporation1</optio ...

  6. CCS3.3下执行优化

    最近想研究一下CCS下的程序优化,之前也了解一些,现在查阅一下资料,整理一下. 当然优化有很多种,我本次先说的是执行优化方式. 首先打开我们自己的工程.工程文件若显示为黄色的,则表示此文件默认优化选项 ...

  7. unity3d 孤岛求生基础案例

    第二个案例,此案例主要实现了第一人称控制器,把移动从世界坐标系转化到人物平面坐标系,通过碰撞器,触发器,光线透射触发器实现交互.实现UI texture记录收集信息,ui texture是更新内容对应 ...

  8. CentOS7安装问题及解决方案记录

    CentOS7系统已安装好: 一.我想要实现系统桌面化. 需要运行 yum 命令. 出现第一个error: 1.提示错误:can not find a valid baseurl 86_X64.... ...

  9. URLError 异常处理

    3 URLError 首先解释下 URLError 可能产生的原因: 网络无连接,即本机无法上网 连接不到特定的服务器 服务器不存在 在代码中,我们需要用 try-except 语句来包围并捕获相应的 ...

  10. javascript 中的继承实现, call,apply,prototype,构造函数

    javascript中继承可以通过call.apply.protoperty实现 1.call call的含义: foo.call(thisObject, args...) 表示函数foo调用的时候, ...