Можно ли разделить коллекцию на мотор?
Таким образом, я использую 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
метод. так что лучше обернуть его при подключении.