一、概述:
Morepath 是一个 Python WSGI 微框架,是模型驱动的。
二、安装:
pip install morepath (CDB python解释器中默认已安装)
三、快速开始:
# 第一种
import morepath
# 继承morepath App基类
class App(morepath.App):
pass
# 创建一个类,作为暴露的资源对象
class Document(object):
def __init__(self, id):
self.id = id
# 绑定路由,返回资源实例
@App.path(path='documents/{id}', model=Document)
def get_document(id):
return Document(id) # query for doc
# 绑定视图函数,self为@App.path中返回的Document实例
@App.html(model=Document)
def hello_doc(self, request):
return'<p>Hello document: %s!</p>'%self.id
if __name__ == '__main__':
morepath.run(App())
# 启动服务:访问127.0.0.1:5000/documents/1
# 第二种
import morepath
class App(morepath.App):
pass
# 直接在类上绑定路由
@App.path(path="")
class Root(object):
pass
@App.view(model=Root)
def hello_world(self, request):
return "Hello world!"
if __name__ == "__main__":
morepath.run(App())
四、Model :
任何类对象都可以作为暴露的资源
五、Paths :
# 如果不指定path则默认为 ''
@App.view(model=Overview)
def overview_default(self, request):
return "Overview"
# path参数指定url
@App.path(model=Overview, path='overview')
def get_overview():
return Overview()
# 获取路径参数
@App.path(model=Document, path='documents/{name}')
def get_document(name):
return query_document_by_name(name)
# 获取query参数,"/documents?name=foo",默认为"test"
@App.path(model=Document, path='documents')
def get_document(name='test'):
return query_document_by_name(name)
# 获取除了text之外的query参数,extra_parameters 为dict
@App.path(model=DocumentCollection, path='search')
def document_search(text='all', extra_parameters):
return DocumentCollection(text, extra_parameters)
# 类型校验,通过默认值指定参数类型,比如id必须为数字,如果传递非数字则会404
@App.path(model=Record, path='records/{id}')
def get_record(id=0):
return record_by_id(id)
def id_converter(id):
return int(id)
# 自定义转换函数:可以针对路径参数,url参数
# 1. 转换逻辑
def id_convert(name):
return str(name)
# 2. 生成一个转换函数
my_converter = morepath.Converter(decode=id_convert)
# 3. 为参数指定转换器
@App.path(model=Document, path='documents/{name}',variables=document_variables, converters=dict(name=my_converter))
def get_document(name):
return Document(name)
# 列表转换器
@App.path(model=Days, path='days', converters=dict(d=[my_converter]))
def get_days(d):
return Days(d)
# 运行时才知道转换器该用哪个,使用get_converters参数
def my_get_converters():
return { 'something': int }
@App.path(path='search', model=SearchResults,
get_converters=my_get_converters)
...
# 设置路径参数id必传: ?id=2
@App.path(model=Document, path='documents', required=['id'])
def get_record(id):
return Document(id)
# 匹配url内容,请求:"/start/foo/bar/baz" ,absorb结果为foo/bar/baz
@App.path(model=Document, path='start', absorb=True)
def get_foo(absorb):
return Model(absorb)
六、反向生成URL:
@App.view(model=Document)
def document_self_link(self, request):
# 目标视图函数无name参数时
return request.link(self')
@App.view(model=Document)
def document_self_link(self, request):
# 目标视图函数包含name参数时
return request.link(self, name='link')
# 反向生成url时,如果存在路径参数或者请求参数,后台默认使用getattr(obj,attr),从目标对象上获取路径参数,
# 可以通过variables参数更加灵活的获取路径参数的
# 例如:默认反向生成url时,直接从obj对象上获取name属性
@App.path(model=Document, path='documents/{name}',
variables=lambda obj: dict(name=obj.name))
def get_document(name):
return query_document_by_name(name)
# 你可以自定义函数
def document_variables(obj):
return dict(name=obj.name)
@App.path(model=Document, path='documents/{name}',
variables=document_variables)
def get_document(name):
return query_document_by_name(name)
# 反向生成url时,统一添加前缀(仅影响反向生成的url,并不会影响系统接口,仅执行一次,后续会使用缓存)
@App.link_prefix()
def simple_link_prefix(request):
return 'http://example.com/'
# 使用model生成link,比起实例消耗更小
@App.view(model=Document, name='class_link')
def document_self_link(self, request):
return request.class_link(Document, variables={'name': 'Document name'})
七、Views
# 返回文本内容 或者 morepath.Response
@App.view(model=User, name='edit',request_method='GET')
def edit_user(self, request):
return "An editing UI goes here"
# 返回html,name参数相当于在@App.path的路由后再添加"/b"
@App.html(name="b" ,model=Document,request_method='GET')
def hello_doc(self, request):
return '<p>Hello document: %s!</p>'%self.id
# 返回json
@App.json(name="a" ,model=Document,request_method='POST')
def hello_doc(self, request):
return {"code":200}
# 指定返回类型
@App.view(model=Document, render=morepath.render_html)
def document_default(self, request):
return "<p>Some html</p>"
# 自定义返回类型
def render_json(content, request):
response = morepath.Response(json.dumps(content))
response.content_type = 'application/json'
return response
@App.view(model=Document, render=morepath.render_json)
def document_default(self, request):
return {'my': 'json'}
# 返回html模板
@App.html(model=Document, template='document.pt')
def document_default(self, request):
return { 'title': self.title, 'content': self.content }
# 添加回调,在回调中设置cookie,而不用自己实例化response
@App.view(model=Document)
def document_default(self, request):
@request.after
def manipulate_response(response):
response.set_cookie('my_cookie', 'cookie_data')
return "document default"
# 合并同一个model的多个view
with App.view(model=Document) as view:
@view()
def document_default(self, request):
return "default"
@view(name="edit")
def document_edit(self, request):
return "edit"
# 自定义路由参数,很像fastapi的依赖
import reg
# 要求请求header中Something=special
@App.predicate(generic.view, name='something', default=None,
index=reg.KeyIndex,
after=morepath.LAST_VIEW_PREDICATE)
def something_predicate(request):
return request.headers.get('Something')
@App.view(model=Document, something='special')
def document_default(self, request):
return "Only if request header Something is set to special."
# request.view 调用路由
@App.json(model=ParticularItem)
def particular_item_default(self, request):
return {'id': self.id}
@App.json(model=Collection)
def collection_default(self, request):
return [request.view(item) for item in self.query()]
# 设置视图只能内部使用,不对外暴露:internal
@App.json(model=SomeOtherItem, name='extra', internal=True)
def some_other_item_extra(self, request):
return self.name
八、Permissions权限控制:
# 定义权限控制类
class Edit(object):
pass
# 权限校验逻辑
@App.view(model=Document, name='edit', permission=Edit)
def document_edit(self, request):
return 'edit document'
@App.permission_rule(model=Document, permission=Edit)
def document_edit_permission(identity, model, permission):
return identity.userid in model.allowed_users
九、Request :
request.GET : query参数
request.POST :Form data
request.method :请求方式
request.cookies : 获取cookie
response.set_cookie :设置cookie
十、重定向:
@App.view(model=User, name='extra')
def redirecting(self, request):
# request.link反向生成url
return morepath.redirect(request.link(self, 'other'))
十一、异常处理:
# 异常捕获处理
from webob.exc import HTTPNotFound
@App.view(model=HTTPNotFound)
def notfound_custom(self, request):
def set_status_code(response):
# 如果不重新设置状态码,状态码会自动为200
response.status_code = self.code # pass along 404
request.after(set_status_code)
return "My custom not found!"
@App.view(model=User, name='extra')
def erroring(self, request):
# 抛出异常
raise HTTPNotFound()
十二、Templates:
from more.chameleon import ChameleonApp
class App(ChameleonApp):
pass
@App.template_directory()
def get_template_directory():
return 'templates'
@App.html(model=Person, template='person.pt')
def person_default(self, request):
return { 'name': self.name }
十三、Configuration:
# 扫描并提交,确保依赖和配置正常
if __name__ == '__main__':
# morepath.scan(more.jinja2) # scan Jinja2 package
# morepath.scan() # scan this package
# 自动扫描
morepath.autoscan()
App.commit()
application = App()
morepath.run(application)
十四、JSON and validation:
# 设置json相应
class Item(object):
def __init__(self, value):
self.value = value
@App.path(model=Item,path="{value}")
def doc_path(value):
return Item(value)
# 定义实例对象返回的json数据
@App.dump_json(model=Item)
def dump_item_json(self, request):
return { 'type': 'Item', 'x': self.value }
#在这里直接返回self,后续会自动调用App.dump_item_json
@App.json(model=Item)
def item_default(self, request):
return self
# 请求数据验证
def my_load(request):
return request.json
@App.json(model=Item, request_method='POST', load=my_load)
def item_post(self, request, obj):
# the third obj argument contains the result of my_load(request)
pass
十五、Settings:
class Sub(App):
pass
# 设置配置
@Sub.setting(section="logging", name="logfile")
def get_logfile_too():
return "/a/different/logfile.log"
# 获取设置
app.settings.logging.logfile
# 从配置文件加载配置
import yaml
with open('settings.yml') as config:
settings_dict = yaml.safe_load(config)
import json
with open('settings.json') as config:
settings_dict = json.load(config)
# 初始化配置
App.init_settings(settings_dict)
morepath.commit(App)
十六、Logging:
directive_logger = logging.getLogger('morepath.directive')
directive_logger.addHandler(logging.StreamHandler())
directive_logger.setLevel(logging.DEBUG)