Python-Eve:防止在不使用唯一字段的情况下插入重复项

Python-Eve: Prevent inserting duplicates without using unique fields

我试图通过以下方法防止插入重复文档:

  1. 从所需端点获取所有文档的列表,其中将包含 JSON 格式的所有文档。此列表称为 available_docs.
  2. 使用 pre_POST_<endpoint> 挂钩以便在插入数据之前处理请求。我没有使用 on_insert 挂钩,因为我需要在验证之前执行此操作。
  3. 因为我们可以访问 request 对象,所以使用 request.json 来获取负载 JSON-formatted
  4. 检查 request.json 是否已经包含在 available_docs
  5. 如果新文档不重复则插入新文档,否则中止。

使用这种方法我得到了以下片段:

def check_duplicate(request):
    if not request.json in available_sims:
        print('Not a duplicate')
    else:
        print('Duplicate')
        flask.abort(422, description='Document is a duplicate and already in database.')

available_docs 列表如下所示:

available_docs = [{'foo': ObjectId('565e12c58b724d7884cd02bb'), 'bar': [ObjectId('565e12c58b724d7884cd02b9'), ObjectId('565e12c58b724d7884cd02ba')]}]

有效负载 request.json 如下所示:

{'foo': '565e12c58b724d7884cd02bb', 'bar': ['565e12c58b724d7884cd02b9', '565e12c58b724d7884cd02ba']}

如您所见,传递给 API 的文档与已存储在数据库中的文档之间的唯一区别是 ID 的数据类型。由于这个事实,我上面代码片段中的 if-statement 评估为 True 并判断要插入的文档不是重复的,而它肯定是重复的。

有没有办法检查传递的文档是否已经在数据库中?我无法使用唯一字段,因为所有文档字段的组合只需要是唯一的。有一个唯一标识符(我在这个例子中遗漏了它),但这不适合所需的比较,因为它是一种时间戳。​​

我认为像在 foobar 上将给定的 ID 转换为 ObjectIDs 可以解决问题,但我不知道该怎么做,因为我知道不知道从哪里获取数据类型 ObjectID

您的方法比为字段设置唯一规则很多。

因为在您的示例中,您要比较 objectid,您不能简单地将它们用作集合的 _id 字段吗?在 Mongo(当然还有 Eve)中,该字段默认是唯一的。实际上,您通常甚至不定义它。您根本不需要做任何事情,因为 POST 具有现有 ID 的文档会立即失败。

如果你不能那样做(也许你需要比较不同的 objectid 字段,但出于某种原因,你仍然不能简单地为该字段设置 unique 规则),我会查看查询数据库的字段值,而不是从数据库中获取所有文档,然后在代码中顺序扫描它们。类似于 db.find({db_field: new_document_field_value})。如果 returns 为真,则新文档是重复的。确保 db_field 已编入索引(这通常也适用于使用 unique 规则标记的字段)

评论后编辑。一个简单的实现可能是这样的:

def pre_POST_callback(resource, request):
    # retrieve mongodb collection using eve connection
    docs = app.data.driver.db['docs']

    if docs.find_one({'foo': <value>}):
        flask.abort(422, description='Document is a duplicate and already in database.')


app = Eve()
app.run()

这是我防止重复记录的方法:

def on_insert_subscription(items): c_subscription = app.data.driver.db['subscription'] user = decode_token() if user: for item in items: if c_subscription.find_one({ 'topic': ObjectId(item['topic']), 'client': ObjectId(user['user_id']) }): abort(422, description="Client already subscribed to this topic") else: item['client'] = ObjectId(user['user_id']) else: abort(401, description='Please provide proper credentials')

我在这里所做的是为客户创建订阅。如果客户已经订阅了一个主题,我会抛出 422。 注意:客户端 ID 是从 JWT 令牌中解码出来的。