Можно ли разделить коллекцию на мотор?

Таким образом, я использую Motor в своем приложении и я ищу создание вспомогательных функций для взаимодействия с базой данных, я изначально изучал использование MongoTurbine, но я склонен против использования ORM, хотя было бы неплохо упростить вызов рутинных функций,

Давайте возьмем upsert например:

Я мог бы написать upsert, используя следующее:

await collection.update_one({'key': value}, {'set': {'key': value}}, upsert = True)

Но было бы проще использовать:

await collection.upsert({'key': value}, {'set': {'key': value}})

Или даже set метод:

await collection.set({'key': value}, {'key': value})

У меня есть немало таких методов, и некоторые другие, но если бы кто-нибудь мог указать мне правильное направление, это было бы здорово!

Триггер, который заставил меня задать этот вопрос, я увидел, что был document_class аргумент в AsyncIOMotorDatabase, который позволяет вам указать тип возвращаемых документов. Но нет простого способа указать собственный класс коллекций.

Я знаю, что все это можно сделать с помощью стандартных соглашений, я просто пытаюсь упростить ситуацию, так как иногда могут быть какие-то длинные фильтры, обновления, проекции, и я просто пытаюсь реализовать свое собственное соглашение.

редактировать

В основном, чтобы добавить больше контекста, я хочу создать пользовательские функции, чтобы уменьшить количество диктов, которые мне нужно писать снова и снова. Возьмите обновление точки пользователя, например; Я хотел бы иметь возможность написать своего рода функцию-обертку вокруг функции update_one, чтобы улучшить удобочитаемость и удобство использования для нескольких модулей.

Пример:

async def update_set(key, value, **kwargs):
    self.update_one({key: value}, {'$set': kwargs})

await db.users.update_set('username', 'johndoe', foo = 1, bar = 'test')
#vs
await db.users.update_one({'username': 'johndoe'}, {'$set': {'foo': 1, 'bar': 'batz'}})

2 ответа

Я пробовал простое создание подклассов, и оно не работает:

class MyCollection(motor_asyncio.AsyncIOMotorCollection):
    pass

my_collection = MyCollection(database=db, name='my_collection')

Traceback (most recent call last):
  File "./test.py", line 13, in <module>
    my_collection = MyCollection(database=db, name='my_collection')
  File "/usr/local/lib/python3.6/dist-packages/motor/core.py", line 528, in __init__
    super(self.__class__, self).__init__(delegate)
TypeError: __init__() missing 1 required positional argument: 'name'

Но с некоторыми уловками это наконец работает:

#!/usr/bin/python3.6

import pymongo
from motor import motor_asyncio


class MyCollection(motor_asyncio.AsyncIOMotorCollection):
    def __init__(self, *args, **kwargs):
        """Calling __init__ of parent class is failing for some reason"""

    def __new__(cls, *args, **kwargs):
        collection = motor_asyncio.AsyncIOMotorCollection(*args, **kwargs)
        collection.__class__ = MyCollection
        return collection

    def my_method(self, *args, **kwargs):
        print('my method', args, kwargs)


client = motor_asyncio.AsyncIOMotorClient()
db = client.db
my_collection = MyCollection(database=db, name='my_collection')
my_collection.my_method()  # my_method () {}
assert db.my_collection == my_collection  # passes assertion

Хотя это не идеальное решение, вы можете достичь этого, написав класс-оболочку для объекта db. В некотором смысле этот класс расширяет объект другого класса вместо самого класса.


db = #get connection 
class Wrapper:
    def __init__(self, obj):
        for key in obj.__dir__():
            # skip internal methods
            if key.startswith("__"):
                continue
            setattr(self, key, getattr(obj, key))

    async def update_set(key, value, **kwargs):
        self.update_one({key: value}, {'$set': kwargs})

db = Wrapper(db)
await db.update_set(...)

Замечания:

Вам нужно использовать обернутый объект, чтобы иметь доступ к update_set метод. так что лучше обернуть его при подключении.

Другие вопросы по тегам