使用Python Eve添加mongoDB document-array-element

Mik*_*utz 6 python rest mongodb eve

背景:(使用Eve和Mongo)

我正在使用Eve REST提供程序库连接和使用mongoDB 从Python中工作,以从数据库中公开许多REST端点.到目前为止,我使用Eve已经好运了,但是我遇到的问题可能超出了Eve本身可以做的事情.

我的问题是我的mongoDb文档格式有一个字段(称为"插槽"),其值是字典/嵌入文档的列表/数组.

所以mongoDB文档结构是:

{
   blah1: data1,
   blah2: data2,
   ...
   slots: [
       {thing1:data1, thing2:data2},
       {thingX:dataX, thingY:dataY}
   ]
}
Run Code Online (Sandbox Code Playgroud)

我需要在'slots'列表中添加新记录(IE添加预先填充的词典).

如果我想通过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"}  }  } )
Run Code Online (Sandbox Code Playgroud)

我想要执行此操作的REST动作/ URI组合是POST'_id/slots',例如URI /app/012345678901234567890123/slots.

问题:(在Eve中将元素插入数组中)

从SO:如何在Python Eve中添加列表类型而不替换旧值eve项目问题看起来Eve目前不支持在mongoDB嵌入式文档(或数组?)上运行,除非整个嵌入式文档被重写,并重写在我的情况下,整个数组非常不受欢迎.


所以,假设它真正的Eve没有一个允许插入数组元素的方法(并且鉴于我已经有许多其他端点在Eve内部运行良好)...


...我现在正在寻找一种方法,在具有多个工作端点的Eve/Flask配置中,拦截并更改Eve的mongoDB写入仅针对这一个端点.

我知道(最坏的情况)我可以覆盖Eve的路由并完​​全手工编写,但后来我会管理_updated和手工检查并更改文档_etag值,这两件事我宁愿不必写新代码.

我看过Eve的Datebase事件挂钩,但是我没有看到修改执行的数据库命令的方法(我可以看到如何更改数据,但不能看到命令).

其他人已经解决了这个问题吗?如果没有关于最直接的手动实施的想法?(希望尽可能多地重复使用Eve,因为我确实希望继续使用Eve来处理所有(已经在工作的)端点)

Nic*_*cci 6

这是个有趣的问题.我相信,为了实现您的目标,您需要执行两项操作:

  1. 构建并传递自定义Validator.
  2. 构建并传递自定义Mongo数据层.

这可能听起来像是太多的工作,但情况可能并非如此.


自定义验证器

将需要自定义验证程序,因为当您在"启用推送"的端点上执行PATCH请求时,您希望传递一个语法,该语句在语法上与端点验证模式不同.您将传递一个dict({"slot": {"thing1": "data1", "thingX": "dataX"}}),而端点需要一个列表:

'mycollection': {
    'type': 'list',
    'schema': {
        'type': 'dict',
        'schema': {
            'thing1': {'type': 'string'},
            'thingX': {'type': 'string'},
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不自定义验证,则最终会出现验证错误(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)
Run Code Online (Sandbox Code Playgroud)

请注意,我没有尝试此代码,因此可能需要在此处进行一些调整.

另一种方法是为PATCH请求设置备用端点.此端点将使用相同的数据源并具有类似dict的架构.这样可以避免使用自定义验证器,并且您仍然可以$set在标准端点上进行正常的原子字段更新().实际上我认为我更喜欢这种方法,因为你不会失去功能并降低复杂性.有关多个端点命中相同数据源的指导,请参阅文档


自定义数据层

这是必需的,因为您要执行PATCH请求中涉及的$push而不是$setwhen mycollection.这样的事情可能是:

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)
Run Code Online (Sandbox Code Playgroud)

把它们放在一起

然后,在应用初始化时使用自定义验证器和数据层:

app = Eve(validator=MyValidator, data=MyMongo)
app.run()
Run Code Online (Sandbox Code Playgroud)

我再次没有测试所有这些; 这是星期天,我在沙滩上,所以它可能需要一些工作,但它应该工作.

说了这么多,我实际上将尝试添加对标准Mongo数据层的推送更新的支持.一对新的全局/端点设置(如MONGO_UPDATE_OPERATOR/)mongo_update_operator在专用分支上实现.前者默认为$set所有API端点仍然执行原子字段更新.人们可以决定某个端点应该执行其他操作,例如a $push.以干净和优雅的方式实现验证有点棘手但是,假设我找到了工作时间,那么这不可能达到Eve 0.6或更高.

希望这可以帮助.