1

我正在使用 PyMongo 和 PyMODM 来处理一个相当简单的文档结构。

这就是我的模型的样子:

class User(MongoModel):
    subscriber_uid: fields.CharField = fields.CharField(required=True)
    interface: fields.CharField = fields.CharField(required=True)
    ...other fields...

    class Meta:
        """Defines MongoDB options for this model"""
        cascade = True  # currently nothing to cascade deletes against
        indexes = [
            IndexModel(
                keys=[('subscriber_uid', pymongo.ASCENDING),
                      ('interface', pymongo.ASCENDING)],
                unique=True,
            ),
        ]

我的单元测试看起来像这样,注意 DB 是一个包装实际 PyMongo 命令的 API。例如:put_user包装user.save()一些异常处理逻辑,而read_user包装User.objects.get().

...
    user = User(**TEST_USER)
    user.interface = '2/2/2/2'
    user.subscriber_uid = 'some other suid'
    DB.put_user(user)
    user_from_db = DB.read_user(subscriber_uid=user.subscriber_uid,
                                interface=user.interface)
    assert user_from_db.interface == user.interface
    assert user_from_db.subscriber_uid == user.subscriber_uid

    # attempt to create a new record with a non-unique suid+iface pair
    # ie: ensure this updates instead of creates
    user = User(**TEST_USER)
    user.someotherattr = 1023
    DB.put_user(user)
    user_from_db: User = DB.read_user(subscriber_uid=user.subscriber_uid,
                                      interface=user.interface)
    assert user_from_db.seed_index == user.seed_index
...

当我运行这个测试时,read_user()/User.objects.get()调用失败并MultipleObjectsReturned返回对象错误,我可以确认有两条记录具有相同的interfacesubscriber_uid值。

从我在其他 StackOverflow答案评论中看到的情况来看,上面的复合唯一索引应该可以防止这种情况发生。

每次测试运行时都会删除数据库,因此问题不在于陈旧的数据挥之不去。我误解了复合指数吗?

4

1 回答 1

1

明显缺乏索引是一个红鲱鱼。

PyMODM 文档状态

请注意,必须先调用 connect()(定义相应的连接别名,如果有),然后才能将任何 MongoModel 与该别名一起使用。如果在 Meta 上定义了索引,则必须在评估 MongoModel 类之前。

我正在使用一个函数来启动与 MongoDB 的连接,这当然是在我导入模型类之后发生的。对于其他数据库客户端,如果您不确定在导入模块时您的数据库是否可用(通常在容器化工作负载中,应用程序启动通常比数据库启动花费更少的时间),那么这通常是要走的路。PyMongo客户端很特别

从 3.0 版开始,MongoClient 构造函数在连接到一个或多个服务器时不再阻塞,如果它们不可用,它不再引发 ConnectionFailure,如果用户的凭据错误,它也不会引发 ConfigurationError。相反,构造函数立即返回并在后台线程上启动连接过程。

将 移动connect()到我的模型模块的顶部解决了这个问题。即使connect()调用时数据库不可用。一旦数据库变得可用,索引就会被创建并且一切都变得正确。

于 2020-05-01T23:44:21.743 回答