Python-Eve:防止在不使用唯一字段的情况下插入重复项
Python-Eve: Prevent inserting duplicates without using unique fields
我试图通过以下方法防止插入重复文档:
- 从所需端点获取所有文档的列表,其中将包含 JSON 格式的所有文档。此列表称为
available_docs
.
- 使用
pre_POST_<endpoint>
挂钩以便在插入数据之前处理请求。我没有使用 on_insert
挂钩,因为我需要在验证之前执行此操作。
- 因为我们可以访问
request
对象,所以使用 request.json
来获取负载 JSON-formatted
- 检查
request.json
是否已经包含在 available_docs
中
- 如果新文档不重复则插入新文档,否则中止。
使用这种方法我得到了以下片段:
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
并判断要插入的文档不是重复的,而它肯定是重复的。
有没有办法检查传递的文档是否已经在数据库中?我无法使用唯一字段,因为所有文档字段的组合只需要是唯一的。有一个唯一标识符(我在这个例子中遗漏了它),但这不适合所需的比较,因为它是一种时间戳。
我认为像在 foo
和 bar
上将给定的 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 令牌中解码出来的。
我试图通过以下方法防止插入重复文档:
- 从所需端点获取所有文档的列表,其中将包含 JSON 格式的所有文档。此列表称为
available_docs
. - 使用
pre_POST_<endpoint>
挂钩以便在插入数据之前处理请求。我没有使用on_insert
挂钩,因为我需要在验证之前执行此操作。 - 因为我们可以访问
request
对象,所以使用request.json
来获取负载 JSON-formatted - 检查
request.json
是否已经包含在available_docs
中
- 如果新文档不重复则插入新文档,否则中止。
使用这种方法我得到了以下片段:
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
并判断要插入的文档不是重复的,而它肯定是重复的。
有没有办法检查传递的文档是否已经在数据库中?我无法使用唯一字段,因为所有文档字段的组合只需要是唯一的。有一个唯一标识符(我在这个例子中遗漏了它),但这不适合所需的比较,因为它是一种时间戳。
我认为像在 foo
和 bar
上将给定的 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 令牌中解码出来的。