Python + MongoDB 文档版本控制

4 投票
3 回答
2512 浏览
提问于 2025-04-16 23:01

我正在开发一个应用程序,主要用于公司内部的项目和任务跟踪。目前我在玩MongoDB(一个数据库)。我心里有一个大概的结构设计:

task
    _id
    name
    project
    initial_notes
    versions
        number
        versions
            version_1
                worker
                status
                date(if submitted)
                review_notes(if rejected)
                reply_on(if accepted/rejected)
            (version_n)(if any)

我遇到的问题是如何管理任务的版本。我看了很多可能的方法,但总是理解得不够透彻。我看到了一些我喜欢的内容,在这里,我很喜欢mongoid的版本管理方式。

经过进一步思考,我更希望它能像这样:

task
    _id
    versions
        number_of_versions: 3
        current_version
            version_no: 3
            worker: bob
            status: accepted
        old_versions
            version
                version_no: 2
                worker: bob

我想在显示任务列表时只展示最新版本,而在查看某个特定任务的详细信息页面时,想要显示该任务的所有版本。这样的结构可行吗?如果可以的话,我需要哪些查询来实现我的需求呢?

感谢你花时间阅读这个问题,也许还能给我一些解答。

状态:拒绝 版本 版本号:1 工作人员:smith 状态:拒绝

3 个回答

0

我之前也遇到过同样的问题,所以我创建了一个叫做HistoricalCollection的工具:

https://pypi.org/project/historical-collection/

这个工具的使用方式和普通的集合差不多,但多了一些额外的方法:

  • patch_one() - 用来更新一个项目
  • patch_many() - 用来批量更新多个项目
  • find_revisions() - 用来查找历史版本
  • latest() - 用来获取最新的版本

下面是一个使用的例子:

from historical_collection.historical import HistoricalCollection
from pymongo import MongoClient
class Users(HistoricalCollection):
    PK_FIELDS = ['username', ]  # <<= This is the only requirement

# ...

users = Users(database=db)

users.patch_one({"username": "darth_later", "email": "darthlater@example.com"})
users.patch_one({"username": "darth_later", "email": "darthlater@example.com", "laser_sword_color": "red"})

list(users.revisions({"username": "darth_later"}))

# [{'_id': ObjectId('5d98c3385d8edadaf0bb845b'),
#   'username': 'darth_later',
#   'email': 'darthlater@example.com',
#   '_revision_metadata': None},
#  {'_id': ObjectId('5d98c3385d8edadaf0bb845b'),
#   'username': 'darth_later',
#   'email': 'darthlater@example.com',
#   '_revision_metadata': None,
#   'laser_sword_color': 'red'}]
3

你也可以考虑这样一个模型:

task
    _id
    ...
    old_versions [
        {
            retired_on
            retired_by
            ...
        }
    ]

这个模型的好处是,你当前的数据总是在最上层(你不需要明确地跟踪当前版本,因为文档本身就是当前版本),而且你可以通过获取当前版本,去掉 old_versions 字段,然后把它加到数据库的 old_versions 字段中,轻松地追踪历史记录。

因为你似乎想要减少网络的输入输出,这样做也能让你在不需要旧版本时,轻松避免加载 old_versions

> db.tasks.find({...}, {old_versions: 0})

你还可以更复杂一点,只存储那些已经改变的字段的旧版本列表。这需要在你的应用层写更精细的代码,如果你不预期会有很多次修改或者这些文档不会很大,可能就没必要这么做。

4

当然可以,为什么不呢?这个方案是可行的。另外,你有没有考虑过这样的做法:

task 
    ...
    versions = [       # MongoDB array 
        {   version_id 
            worker
            status
            date(if submitted)
            review_notes(if rejected)
            reply_on(if accepted/rejected)
        },
        { version_id : ... }, 
        ... 

这是一个可能的版本插入查询:

tasks.update( { # a query to locate a particular task}, 
              { '$push' : { 'versions', { # new version } } } )

请注意,在这种情况下,从版本数组中获取最后一个版本是由程序来完成的,而不是由Mongo数据库来处理的。

撰写回答