umongo, pymongo, python 3, 我如何从参考字段加载数据

umongo, pymongo, python 3, how do i load data from reference field(s)

我试图了解如何以及为什么加载我的引用数据如此困难,在 unmongo/pymongo

@instance.register
class MyEntity(Document):
    account = fields.ReferenceField('Account', required=True)
    date = fields.DateTimeField(
        default=lambda: datetime.utcnow(),
        allow_none=False
    )
    positions = fields.ListField(fields.ReferenceField('Position'))
    targets = fields.ListField(fields.ReferenceField('Target'))

    class Meta:
        collection = db.myentity

当我通过以下方式检索时:

    def find_all(self):
        items = self._repo.find_all(
            {
                'user_id': self._user_id
            }
        )
        return items

然后像这样转储它:

    from bson.json_util import dumps

    all_items = []
    for item in all_items:
        all_items.append(item.dump())


    return dumps(all_items)

我得到以下 JSON 对象:

[
  {
    "account": "5e990db75f22b6b45d3ce814",
    "positions": [
      "5e9a594373e07613b358bdbb",
      "5e9a594373e07613b358bdbe",
      "5e9a594373e07613b358bdc1"
    ],
    "date": "2020-04-18T01:34:59.919000+00:00",
    "id": "5e9a594373e07613b358bdcb",
    "targets": [
      "5e9a594373e07613b358bdc4",
      "5e9a594373e07613b358bdc7",
      "5e9a594373e07613b358bdca"
    ]
  }
]

没有dump

<object Document models.myentity.schema.MyEntity({
'targets':
    <object umongo.data_objects.List([
        <object umongo.frameworks.pymongo.PyMongoReference(
            document=Target,
            pk=ObjectId('5e9a594373e07613b358bdc4')
            )>,
        <object umongo.frameworks.pymongo.PyMongoReference(
            document=Target,
            pk=ObjectId('5e9a594373e07613b358bdc7')
            )>,
        <object umongo.frameworks.pymongo.PyMongoReference(
            document=Target,
            pk=ObjectId('5e9a594373e07613b358bdca'))>]
            )>,
            'id': ObjectId('5e9a594373e07613b358bdcb'),
'positions':
    <object umongo.data_objects.List([
        <object umongo.frameworks.pymongo.PyMongoReference(
            document=Position,
            pk=ObjectId('5e9a594373e07613b358bdbb')
        )>,
        <object umongo.frameworks.pymongo.PyMongoReference(
            document=Position,
            pk=ObjectId('5e9a594373e07613b358bdbe'))>,
        <object umongo.frameworks.pymongo.PyMongoReference(
            document=Position,
            pk=ObjectId('5e9a594373e07613b358bdc1'))>])>,
'date': datetime.datetime(2020, 4, 18, 1, 34, 59, 919000),
'account': <object umongo.frameworks.pymongo.PyMongoReference(document=Account, pk=ObjectId('5e990db75f22b6b45d3ce814'))>
})>
  1. 我真的很纠结如何取消引用它。如果我在 umongo 模式中指定它们,我希望递归地取消引用所有加载的字段。这不是在umongoAPI吗?

即如果 'target' 中也有引用字段怎么办?我知道这在数据库上可能很昂贵,但是有什么方法可以在模式定义本身上指定它吗?即在 meta class 中,我总是想要特定字段的完整、取消引用的对象?

  1. 事实是我发现关于这个的文档/评论很少,甚至在 umongo 文档中都没有提到,以及我找到的其他 ODM 的一些解决方案(比如 mongoengine) 正在痛苦地写出每个字段/每个查询的递归手动函数。这对我来说表明这不是一个受欢迎的问题是有原因的。可能是反模式?如果是这样,为什么?

我不是 mongo 的新手,而是 python / mongo 的新手。我觉得我在这里缺少一些基本的东西。


编辑:发布后,我确实发现了这个问题:

https://github.com/Scille/umongo/issues/42

提供了前进的方向

这仍然是最好的方法吗?仍在尝试理解为什么将其视为边缘案例。


编辑 2:进度

class MyEntity(Document):
    account = fields.ReferenceField('Account', required=True, dump=lambda: 'fetch_account')
    date = fields.DateTimeField(
        default=lambda: datetime.utcnow(),
        allow_none=False
    )
    #trade = fields.DictField()
    positions = fields.ListField(fields.ReferenceField('Position'))
    targets = fields.ListField(fields.ReferenceField('Target'))

    class Meta:
        collection = db.trade

    @property
    def fetch_account(self):
        return self.account.fetch()

所以使用新定义的 属性 装饰器,我可以做到:

    items = MyEntityService().find_all()
    allItems = []
    for item in allItems:
        account = item.fetch_account
        log(account.dump())
        allItems.append(item.dump())

当我转帐时,一切都很好。但我不想 explicitly/manually 必须这样做。这仍然意味着我每次进行查询时都必须递归地解包然后重新打包每个引用的文档和任何子引用。这也意味着模式 SOT 不再仅包含在 umongo class 中,即如果字段发生变化,我将不得不重构使用该字段的每个查询。

我仍在寻找在模式本身上 decorate/flag 的方法。 例如

    account = fields.ReferenceField('Account', required=True, dump=lambda: 'fetch_account')

dump=lambda: 'fetch_account' 我只是编造的,它什么也没做,但这或多或少是我想要的模式,不确定这是否可能(甚至聪明:其他方向,指针欢迎回答为什么我的方法完全错误)....


编辑 3: 所以这是我登陆的地方:

    @property
    def fetch_account(self):
        return self.account.fetch().dump()

    @property
    def fetch_targets(self):
        targets_list = []
        for target in self.targets:
            doc = target.fetch().dump()
            targets_list.append(doc)
        return targets_list

    @property
    def fetch_positions(self):
        positions_list = []
        for position in self.positions:
            doc = position.fetch().dump()
            positions_list.append(doc)
        return positions_list

然后访问:

    allItems = []
    for item in items:
        account = item.fetch_account
        positions = item.fetch_positions
        targets = item.fetch_targets

        item = item.dump()
        item['account'] = account
        item['positions'] = positions
        item['targets'] = targets
        # del item['targets']
        allTrades.append(item)

我可以清理它 up/abstract 一些,但我不知道此时我如何才能真正减少一般的冗长。它似乎确实给了我我正在寻找的结果:

[
  {
    "date": "2020-04-18T01:34:59.919000+00:00",
    "targets": [
      {
        "con_id": 331641614,
        "value": 106,
        "date": "2020-04-18T01:34:59.834000+00:00",
        "account": "5e990db75f22b6b45d3ce814",
        "id": "5e9a594373e07613b358bdc4"
      },
      {
        "con_id": 303019419,
        "value": 0,
        "date": "2020-04-18T01:34:59.867000+00:00",
        "account": "5e990db75f22b6b45d3ce814",
        "id": "5e9a594373e07613b358bdc7"
      },
      {
        "con_id": 15547841,
        "value": 9,
        "date": "2020-04-18T01:34:59.912000+00:00",
        "account": "5e990db75f22b6b45d3ce814",
        "id": "5e9a594373e07613b358bdca"
      }
    ],
    "account": {
      "user_name": "hello",
      "account_type": "LIVE",
      "id": "5e990db75f22b6b45d3ce814",
      "user_id": "U3621607"
    },
    "positions": [
      {
        "con_id": 331641614,
        "value": 104,
        "date": "2020-04-18T01:34:59.728000+00:00",
        "account": "5e990db75f22b6b45d3ce814",
        "id": "5e9a594373e07613b358bdbb"
      },
      {
        "con_id": 303019419,
        "value": 0,
        "date": "2020-04-18T01:34:59.764000+00:00",
        "account": "5e990db75f22b6b45d3ce814",
        "id": "5e9a594373e07613b358bdbe"
      },
      {
        "con_id": 15547841,
        "value": 8,
        "date": "2020-04-18T01:34:59.797000+00:00",
        "account": "5e990db75f22b6b45d3ce814",
        "id": "5e9a594373e07613b358bdc1"
      }
    ],
    "id": "5e9a594373e07613b35
8bdcb"
  }
]

这似乎是 umongo 中的一个设计选择。

例如在 Mongoid 中(MongoDB 的 Ruby ODM),当一个对象被引用时,它会根据需要通过关联自动从数据库中获取。

顺便说一句,在 ODM 中,"define a field structure" 和 "seamlessly access data through application objects" 的功能完全不同。例如,我在 Java 中使用 Hibernate 的经验表明它与您在 umongo 中发现的相似 - 一旦数据被加载,它提供了一种使用应用程序定义的字段结构和类型等访问数据的方法,但它并不能真正帮助透明地从应用程序域加载数据。