背景:(使用 Eve 和 Mongo)
我正在使用 Python 使用 Eve REST 提供程序库连接并连接到 mongoDB 以从数据库公开许多 REST 端点。到目前为止,我在使用 Eve 时运气不错,但我遇到了一个问题,这可能超出了 Eve 本身所能做的范围。
我的问题是我的 mongoDb 文档格式有一个字段(称为“插槽”),其值是字典/嵌入文档的列表/数组。
所以mongoDB的文档结构是:
{
blah1: data1,
blah2: data2,
...
slots: [
{thing1:data1, thing2:data2},
{thingX:dataX, thingY:dataY}
]
}
我需要将新记录(即添加预填充的字典)添加到“插槽”列表中。
如果我想象直接通过 pymongo 进行插入,它看起来像:
mongo.connection = MongoClient()
mongo.db = mongo.connection['myDB']
mongo.coll = mongo.db['myCollection']
...
mongo.coll.update({'_id' : document_id},
{'$push': { "slot" : {"thing1":"data1","thingX":"dataX"} } } )
我想要执行此操作的 REST 操作/URI 组合是到“_id/slots”的 POST
,例如/app/012345678901234567890123/slots
的URI。
问题:(在 Eve 中将元素插入到数组中)
从 SO:How to add to a list type in Python Eve without replacing old values 和 eve project issue 看来,Eve 目前不支持对 mongoDB 嵌入式文档(或数组?)进行操作,除非整个嵌入式文档被重写,并且在我的情况下重写整个数组是非常不可取的。
所以,假设它真正的 Eve 没有允许插入数组元素的方法(并且鉴于我已经有许多其他端点在 Eve 内部运行良好)...
...我现在正在寻找一种方法,在具有多个工作端点的 Eve/Flask 配置中,仅为这个端点拦截和更改 Eve 的 mongoDB 写入。
我知道(最坏的情况)我可以覆盖 Eve 的路由并完全手动编写,但是我会管理 _updated
并手动检查和更改文档 _etag
值,我当然不希望为此编写新代码。
我查看了 Eve 的 Datebase event hooks,但没有看到修改执行的数据库命令的方法(我可以看到如何更改 data,但不能更改 commands )。
其他人已经解决了这个问题吗?如果没有关于手动实现的最直接路径的任何想法? (希望尽可能多地重用 Eve,因为我确实想继续将 Eve 用于我所有(已经工作的)端点)
最佳答案
这是一个有趣的问题。我相信,为了实现您的目标,您需要执行两项操作:
- 构建并传递自定义验证器。
- 构建并传递自定义 Mongo 数据层。
这听起来可能工作量太大,但可能并非如此。
自定义验证器
将需要一个自定义验证器,因为当您在“启用推送”的端点上执行 PATCH 请求时,您希望传递一个在语法上与端点验证模式不同的文档。您将传递一个 dict ({"slot": {"thing1": "data1", "thingX": "dataX"}}
) 而端点需要一个列表:
'mycollection': {
'type': 'list',
'schema': {
'type': 'dict',
'schema': {
'thing1': {'type': 'string'},
'thingX': {'type': 'string'},
}
}
}
如果您不自定义验证,最终会出现验证错误(list type expected
)。我猜您的自定义验证器可能类似于:
from eve.data.mongo.validation import Validator
from flask import request
class MyValidator(Validator):
def validate_replace(self, document, _id, original_document=None):
if self.resource = 'mycollection' and request.method = 'PATCH':
# you want to perform some real validation here
return True
return super(Validator, self).validate(document)
请注意,我没有尝试此代码,因此可能需要在这里和那里进行一些调整。
另一种方法是为 PATCH 请求设置一个替代端点。该端点将使用相同的数据源并具有类似字典的模式。这将避免对自定义验证器的需求,而且您仍然可以在标准端点上进行正常的原子字段更新 ($set
)。实际上,我认为我更喜欢这种方法,因为您不会失去功能并降低复杂性。有关访问同一数据源的多个端点的指南,请参阅 the docs
自定义数据层
这是必需的,因为当 mycollection
涉及 PATCH 请求时,您希望执行 $push
而不是 $set
。可能是这样的:
from eve.io.mongo import Mongo
from flask import request
class MyMongo(Mongo):
def update(self, resource, id_, updates, original):
op = '$push' if resource == 'mycollection' else '$set'
return self._change_request(resource, id_, {op: updates}, original)
把它们放在一起
然后您在应用初始化时使用您的自定义验证器和数据层:
app = Eve(validator=MyValidator, data=MyMongo)
app.run()
我再次没有测试所有这些;今天是星期天,我在海滩上,所以它可能需要一些工作,但它应该工作。
话虽如此,我实际上将尝试向标准 Mongo 数据层添加对推送更新的支持。一对新的全局/端点设置,如 MONGO_UPDATE_OPERATOR
/mongo_update_operator
在私有(private)分支上实现。前者默认为 $set
所以所有 API 端点仍然执行原子字段更新。可以决定某个端点应该执行其他操作,比如 $push
。以一种简洁优雅的方式实现验证有点棘手,但假设我有时间研究它,这不太可能让它达到 Eve 0.6 或更高版本。
希望这会有所帮助。
关于python - 使用 Python Eve 添加 mongoDB 文档数组元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30537070/