Flask备注4(Structure)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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结构程序的步骤如下:

  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/idGET获取单个用户
/users/idPUT更新单个用户
/users/idDELETE删除单个用户

通过继承自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 备注一(单元测试,Debugger, Logger)

flask框架3

flask-Memcached缓存系统

Flask源码阅读-第二篇(flask\__init__.py)

来自 Flask-MongoKit 的元类错误

flask项目结构