将pyramid、sqlalchemy、simplejson粘合在一起,以提供一个读写的、支持对象图的json api
py-liant的Python项目详细描述
简介
py liant是快速创建自以为是的restfulapi的助手库。 使用金字塔和sqlalchemy。它使用 一个稍微修改的对象图感知json结构,它紧密耦合 随着数据模型的曝光。
它是由内部项目的trip解决方案创建的,但我们认为它可能会证明 对一般消费有用。
restful api
基类假设api遵循rest约定 并提供crud([c]reate,[r]ead,[u]pdate,[d]elete)功能,或者 一个子集。它不会对端点做任何假设,这些端点是 仍然在用户代码中定义。对于 有效载荷,请参见修改后的json和crudview。
固执己见
但是,基类提供了一个自定义解析器 对于url字符串和api的结构有很大的自以为是。 这使得它可以轻松地部署在现有的sqlalchemy数据之上 结构,但缺点是可定制性较差。
修改后的json
orm数据模型并不总是树。任何超出 一定的复杂程度必然会导致映射深层数据 直接面向json的模型不是 可行。 在我们的第一次迭代中,我们通过手动分离来解决这个问题 结构中的json,但是任何手动过程都会很快变成一个时间 sink;它为客户端和服务器代码增加了许多复杂性。
py liant通过为内部保留两个关键字来解决图形感知问题
在json图中使用。需要从
json结构将获得一个带有生成值的特殊键\u id
。工具书类
使用带有sigle的对象对对象进行编码匹配
引用对象的id。请注意,这只适用于sqlalchemy
模型对象。
例如,给定下面的模型声明:
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
下面的代码片段
frompy_liant.json_encoderimportJSONEncoderencoder=JSONEncoder(base_type=Base,check_circular=False,indent=4*' ')parent=Parent(id=1,data="parent object")parent.children.extend([Child(id=1,data="child 1"),Child(id=2,data="child 2")])print(encoder.encode(parent))
将输出
{"id":1,"data":"parent object","children":[{"id":1,"data":"child 1","parent":{"_ref":1},"_id":2},{"id":2,"data":"child 2","parent":{"_ref":1},"_id":3}],"_id":1}
编码器还将从sqlalchemy模型中提取元数据信息 支持序列化。它将只序列化列和关系 属性,这意味着它不会显示任何非sqlalchemy属性。它 也希望所有的关系都能被急切地加载,并避免触发 任何延迟加载的属性。也避免了延迟列。
相反,jsondecoder将变成一个简单编码的json。 构造并返回一个完整的图,具有潜在的循环或多个 参考资料,供应用程序使用。
解码器将生成一个结构 类是使用修补程序sqlalchemy base class进行修补的, 解码后的对象可用于修补现有或新的sqlalchemy模型 实例。
我们还提供了一对用于javascript的编码器/解码器函数 在pyliant.js中。
如何使用
在金字塔的配置块中,可以使用 以下内容:
frompy_liant.pyramidimportpyramid_json_renderer_factoryconfig.add_renderer('json',pyramid_json_renderer_factory(Base))
然后在任何@view_config()
或add_view()
中使用renderer='json'
。
通过在金字塔的配置中添加以下内容,可以使用py liant的json解码器:
frompy_liant.pyramidimportpyramid_json_decoderconfig.add_request_method(pyramid_json_decoder,'json',reify=True)
因此,对于主体中包含json负载的任何请求,您都可以访问解码后的
jsonobject结构使用request.json
修补sqlalchemy模型的基类:
patch_sqlalchemy_base_class(Base)
添加视图谓词:
config.add_view_predicate('convert_matchdict',ConvertMatchdictPredicate)config.add_view_predicate('catchall',CatchallPredicate)
py liant还提供一个可调用的工厂来执行上述所有操作:
frompy_liant.pyramidimportincludeme_factoryconfig.include(includeme_factory(base_class=Base))# identical to includeme_factory(base_class=Base)(config)
crudview的具体用法示例 在参考文档中可以找到
参考
jsonobject
这个类是一个dict
实现,它将所有字符串键公开为
性质。它消除了使用索引访问字典值的需要
表示法(request.json['prop']
变成request.json.prop
)。这个
jsondecoder返回此类的实例。
jsonencoder
asimplejson.jsonencoder
实现,添加以下内容:
- 将
日期
、时间
和日期时间
对象转换为ISO8859字符串 - 将
字节
值转换为base64 - 将python
enum
值限制为它们的名称,uuid.uuid
值 - 跟踪SqlAlchemy模型(如果提供基类),如修改后的JSON中所述
构造函数参数:
JSONDecoder(request=None,base_type=None,**kwargs)
请求
应为棱锥体请求对象。如果它用于
jsonguardprovider序列化的围栏。
base_type
是sqlalchemy模型基类。如果没有提供
与sqlalchemy相关的功能被禁用。
kwargs
被传递给simplejson.jsonencoder
的构造函数
jsondecoder
返回
结果是,jsonobject并处理 构造函数参数: 一个金字塔渲染器的工厂使用
jsonencoder。有关用法,请参见如何使用。 参数: 这是一个功能,可以添加到金字塔使用
这是添加方法的函数
将更改应用到sqlalchemy的基类中。 一旦sqlalchemy的基类使用
patch sqlalchemy_base_class所有模型实例都将获得
可用于应用修补程序的方法。这可以直接使用,但大多数
如果您使用crudview和/或
catchallview,您不必。 该方法将在所需的任何深度应用更改。它转换数据类型
基于从sqlalchemy中提取的元数据。它处理关系,两者
集合和实例,通过跟踪和比较json中提供的主键。如果需要,它将添加新实例。 对于没有关系的对象,它将 如果对象具有关系,则 如果子体的主键是包含
外键中的列调用方可以提供部分主键和
py liant将根据与
父母, 如果提供了实现
jsonguardprovider,它将用于安全围栏
修补。 该类为给定的模型类提供CRUD功能。你可以
根据应用程序的需要配置路由和视图,但是
推荐方式如下: 这足以为类型为的对象提供完整的读写端点
使用 使用 在id=1的父实例中更新数据。 过帐到 最后, 对于列表端点,将返回以下响应: 克鲁德维尤类还提供分页支持,隐式和显式
过滤、隐式和显式排序。 通过get parameters 为所有列属性提供隐式筛选器和排序。假设
列属性 还将添加自动过滤器(在上面的示例用法中,请参见调用
列表端点中的过滤是这样完成的: 排序是通过使用get参数 排序和筛选键也可以手动定义。在上面的使用示例中,我们可以手动定义一些筛选器和排序: 这样做显然更费劲,但允许您定义自定义筛选器或排序表达式。 该实现假定 如果棱锥体已配置为使用此谓词,如如何使用
使用可以避开转换matchdict参数的需要。 金字塔的url
调度<
文档页显示以下URL MatchDict转换示例: 这段代码确保除非谓词执行,否则路由将不匹配
成功(返回 为了用支持的机制替换此功能,我们实现了
泛型新样式路由谓词类。在你的路线中使用这个类
首先必须按照如何使用中所述配置它。然后在
上一节中视图为route 请注意,在旧的 在这些更改之后,您不再需要在
这是一个支持谓词,可与catchallview一起使用。它
假设路由包含 这是crudview类的一个扩展,添加了对
更丰富的路由格式基于由catchAllPredicate完成的内部解析,并且能够: 使用这个类: 此代码足以公开如下路由: 换句话说,可以从
单点。 但是从应用程序的角度来看
这些提示还允许您通过
推迟他们。即,如果您在 如果我们还为 提示可以具有任意深度。每个关系提示都可以有提示
指的是这种关系的实体。 提示也适用于列出请求: 请注意:动态关系属性不能是
关系提示。 如果打电话的人只想找回父母的孩子不知道他们是谁
可以调用 向下钻取同时支持正常关系属性和动态关系属性
关系属性。它自动确定目标属性是否为
列表或单个实体(即 如果正在钻取的属性是集合all,则过滤、排序和
分页注意事项适用。 如果请求通过
钻取或引用实体集合,因为它
不包含主键说明符调用方可以选择单个项
从列表中使用下标符号。例如, 过滤、排序和分页的应用如
crudview部分。仅使用 也支持由crudview支持的分页,但是
上一节中描述的下标符号可用于
切片: 出于安全考虑,此库提供的灵活性可以是
有害的。模型类可以包含对需要
从读取它们(当使用
就更新它们而言(任何
插入/更新方法)。 jsonguardprovider接口允许您为四个
领域: 使用jsonguardprovider在金字塔中实现这个接口
上下文
然后将其连接到路线并使用 例如: 这是postgresql特有的附加功能,可用于设置架构
搜索所有新创建的数据库连接的路径。它的实现方式是
sqlalchemy pythonenum是sqlalchemy.types.enum的自定义实现,即
在PostgreSQL中用于声明命名枚举类型。 用法:/
逻辑
在修改后的json中描述。
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
0
**kwargs
被传递给simplejson.jsondecoder
的构造函数。金字塔json渲染器工厂
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
1
基本类型
和分隔符
传递给
构造器。分隔符的默认值是为了最小化负载
跳过任何不必要的空格来调整大小。
wsgi-iter
可以通过传递iterable来优化json的呈现
直接到wsgi层。默认情况下,渲染器直接在
金字塔响应
对象。当激活的棱锥体不能再处理错误时
重定向序列化期间引发的执行选项。
金字塔JSON解码器
配置添加请求方法
。有关用法,请参见如何使用。修补程序sqlalchemy基础类
monkeypatch:obj.应用更改
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
2
data
中的值应用到
在obj
中对应的列属性。没有覆盖属性值
除非在数据
对象中指定。数据
对象可以深入到这些关系中。为了
集合关系apply_changes
方法希望所有对象
在相应的数组中提供,至少使用它们的主键
现在。如果数组的成员不提供主键,则假定
成为一个新的例子。如果无法跟踪对象集合的成员
返回数据中的数组成员,它将从集合中移除。crudview
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
3
父级
get/parent/1http/1.1
检索id=1的父项。它应该会回来
大致如下:fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
4
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
5
/parent
而不是/parent/1
将创建一个新实例
更新现有的。删除/parent/2http/1.1
将删除id=2的父项。get/parent http/1.1
将提供
数据库。fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
6
page
和pagesize
支持分页(即get/parent?页码=3&pagesize=20
)。id
和data
对于类用户,以下过滤器将是
添加到self.filters
(在上面的示例用法中,在构造期间,请参见
auto_filters()
调用:id,id_lt,id_le,id_gt,id_ge,data,data_lt,
数据,数据,数据,数据。筛选器[字段名][运算符]
使用小于、小于或等于、大于
大于或等于并包含运算符。最后一个是自动生成的
仅用于字符串列属性。auto_order()
)两个字段。get/parent?data_like=对象
。
可以应用多个过滤器,即get/parent?id_lt=10&id_gt=5
order
完成的,即get/parent?顺序=数据
。可以应用多个排序表达式,即
顺序=数据,id
。换句话说,顺序中传递的值是逗号分隔的
排序键列表。每个排序键也接受降序修饰符,
即
order=data+desc,id
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
7
request.dbsession
是一个返回
对模型有效的sqlalchemy数据库会话。convertMatchDictPredicate
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
8
true
)并且视图将看到键的整数值
请求.matchdict
中的年
,月
和日
。虽然这很有用
不幸的是不推荐使用的功能。Sice金字塔-1.5你将得到
在路由或视图中使用自定义谓词时,将发出弃用警告。
parent\u pk配置的示例
更改如下:
fromsqlalchemy.ormimportrelationship,backreffromsqlalchemyimportColumn,Integer,Text,ForeignKeyfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classParent(Base):__tablename__='parent'id=Column(Integer,primary_key=True)data=Column(Text)classChild(Base):__tablename__='child'id=Column(Integer,primary_key=True)parent_id=Column(ForeignKey(Parent.id))data=Column(Text)parent=relationship(Parent,backref=backref('children'))
9
custom_谓词中
matchdict参数是在路由级别完成的,新样式的路由谓词
无法访问matchdict。因此,我们必须使用视图谓词
实现同样的目标。
identity_filter()
方法。您还可以避免捕获valueerror
例外。catchallpredicate
{catchall:.}
(不是
*catchall
},因为star格式从
匹配)然后在内部分析并转换为更适合catchAllView类的值。catchallview
frompy_liant.json_encoderimportJSONEncoderencoder=JSONEncoder(base_type=Base,check_circular=False,indent=4*' ')parent=Parent(id=1,data="parent object")parent.children.extend([Child(id=1,data="child 1"),Child(id=2,data="child 2")])print(encoder.encode(parent))
0
get/parent
或get/child
列出所有父级或子级
get/parent@1
或get/child@1
获取id=1的父级或id=1的子级
post/parent
或post/child
添加新父级
post/parent@1
更新id=1的父属性
删除/parent@1
,删除/child@1
删除id=1的父级或
ID=1 提示语法
child
在根级别可能不是什么有用的东西,换句话说
可能希望您的api将子项
视为与父项
紧密绑定。卡塔查维尤
允许您使用get/parent@1:*children
一次性获取父实体和所有子实体。CatchallView将看到路线的一部分
作为父实体的加载提示列表的列字符之后。在
在这种情况下,它会在查询中附加一个
selectinload(parent.children)
选项。父节点和调用方上添加了
blob
属性
为了避免检索,他们可以调用get/parent@1:-blob
。
相反,如果blob
属性在模型中被mared为延迟列
声明,但调用者希望它包含在他们可以包含的响应中
通过调用get/parent@1:+blob
子实体添加了
blob
列(假设它是
代码中的延迟列),调用者可以得到包含所有子项的父项
通过调用get/parent@1:*children(+blob)
为每个对象包含blob。
通过逗号分隔可以提供多个提示。这也是事实
对于关系提示:get/parent@1:-blob,*children
表示"loadparent
和allchildren
包含并延迟加载列parent.blob
"get/parent@1:-blob,*子节点(+blob,-blob2,*第二个父节点)
表示"加载父节点"
包含所有的子节点后,延迟列
parent.blob
和child.blob2
并
取消定义columnchild.blob
。对于每个子对象,还加载关系child.second_parent
get/parent:*children
will
有效地检索所有父级和所有关联的子级。向下钻取支架
get/parent@1/children
。路线的最后一点不是提示,
它是一个钻取说明符。这将构造一个查询,检索所有子项
对于id=1的父级,通过读取关系的外键约束
父级.子级
get/child@1/parent
也可以)。所有提示
前提是必须在钻取说明符之后,它们将引用
关系中的实体被钻入。例如在请求中
get/parent@1/children:+blob
提示将延迟加载列
子.blob
集合中的单个元素
get/parent@1/children[0]
将检索parent.children的第一个子
收集。应用过滤和排序
首先,
过滤、排序、分页
auto_filters
和auto_order
。
即将支持自定义表达式。get/parent[0:10]?order_by=data+desc
检索前10个父节点
降序实体数据
顺序。
jsonguardprovider
guardupdate
允许您控制可以写入
实体只要obj.apply_changes()
get called
guardrilldown
允许您控制可以是什么属性catchallview
add_route
的工厂查看
frompy_liant.json_encoderimportJSONEncoderencoder=JSONEncoder(base_type=Base,check_circular=False,indent=4*' ')parent=Parent(id=1,data="parent object")parent.children.extend([Child(id=1,data="child 1"),Child(id=2,data="child 2")])print(encoder.encode(parent))
1
搜索路径设置器
poollistener
(自0.7版以来已弃用)。替代品
使用当前正在开发的现代事件API。Enumattrs和pythonenum
frompy_liant.json_encoderimportJSONEncoderencoder=JSONEncoder(base_type=Base,check_circular=False,indent=4*' ')parent=Parent(id=1,data="parent object")parent.children.extend([Child(id=1,data="child 1"),Child(id=2,data="child 2")])print(encoder.encode(parent))
2
推荐PyPI第三方库