蓝图实例
from pprika import Blueprint main = Blueprint('main', '/animals') @main.route('/cat') def get_meow(): return '喵喵!!是猫咪'
蓝图的初始化需要提供至少一个参数
class Blueprint(object): def __init__(self, name, url_prefix=None): if url_prefix is None: url_prefix = '/' + name self.name = name self.url_prefix = url_prefix self._deferred_funcs = []
name为该蓝图的名称,是之后路由时的依据,需要每个蓝图都不一样。而url_prefix默认为name,它用于附加到path之前,划分不同蓝图的作用域?如上例所对应的请求url则为 http://xy.z/animals/cat。_deferred_funcs 的用途后面会提到。
蓝图的路由及请求处理几乎与PPrika类一致,如上述route注册方式等同于 main.add_url_rule('/cat', 'get_meow', get_meow) ,因此直接讲add_url_rule。
add_url_rule
def add_url_rule(self, path, endpoint=None, view_func=None, **options): path = "/".join((self.url_prefix.rstrip("/"), path.lstrip("/"))) if view_func and hasattr(view_func, "__name__"): assert ( "." not in view_func.__name__ ), '视图函数名不应带"."' if endpoint: assert "." not in endpoint, 'endpoint参数不可带"."' if endpoint is None: assert view_func is not None, "无endpoint时view_func不可为空" endpoint = view_func.__name__ endpoint = f'{self.name}.{endpoint}'
上面这一大段其实只做了三件事:附加 url_prefix 到path前、确保视图函数名不能带点(dot)、将endpoint调整为 name.xx 的点分结构。因为blueprint的路由请求响应本质还是通过app进行的,它只是提供了代码更好的组织方式(还有相对独立的错误处理),所以只要处理好参数转发给app即可。 ↓
# 函数通过deferred_funcs转发,等到有了app再执行(注册rule) self._deferred_funcs.append( lambda a: a.add_url_rule(path, endpoint, view_func, **options) )
最后用之前处理好的参数调用PPrika类的同名方法即可,但由于此时app未必存在,因此需要通过 self._deferred_funcs 先将该操作保存起来,等后面把blueprint注册到app上时再调用。
同样的,blueprint上的error_handler等也是这样,最终都将注册操作暂存到_deferred_funcs 上,等app出现再转发给它,这里不多赘述。
register
def register(self, app): for f in self._deferred_funcs: f(app)
比起flask,pprika的蓝图注册就只是调用之前暂存的那些lambda函数,省去了static、template等不少处理,而且由于省去了flask.BlueprintSetupState这一中间层,url_prefix在蓝图对象实例化后就不可再变。
而这个register函数又由PPrika.register_blueprint调用↓
PPrika.register_blueprint
def register_blueprint(self, blueprint): bp_name = blueprint.name if bp_name in self.blueprints: assert blueprint is self.blueprints[bp_name], f""" 已存在名为 {bp_name} 的blueprints:{self.blueprints[bp_name]}, 请确保正在创建的 {blueprint} 名称唯一 """ else: self.blueprints[bp_name] = blueprint blueprint.register(self)
做了两件事:根据app.blueprints保存的蓝图名与蓝图对象映射确保每个蓝图名称都唯一,在此基础上将这次注册的蓝图记入app.blueprints并调用蓝图上的register完成后续注册过程。
这样一个蓝图对象就成功注册到app上了,之后蓝图上的请求、错误实质都会用app上之前介绍的那些函数处理,与一般app注册的视图没什么差别。
结语
本篇的blueprint.Blueprint是之后restful.Api实现的基础,可以说Api就是一个增强的Blueprint
之后预计由两篇讲述restful功能的实现
[python] pprika:基于werkzeug编写的web框架(6) ——restful的错误处理