pymongo - 使用自定义游标类

时间:2012-10-11 15:02:06

标签: iterator pymongo

pymongo是否有可能让collection.find()方法返回一个继承自base的自定义游标类,但是重新定义了迭代的发生方式?

我想在迭代时从光标内的mongo数据中实例化特定于app的模型。这些文档有一个type attr,用于确定应该创建哪种实例。我在想这个next方法可以查看这个数据并确定要创建和返回的类型。继承光标很容易,但我无法弄清楚将其挂钩到find()操作的哪个位置?

编辑或者......

我目前正在做的是使用yield吐出一个生成器,该生成器会在执行提取后对对象进行分类。

@classmethod
def gather(cls,place_id):
    """
    Gather instances of all the shouts for one place
    """
    shouts = cls.collection.find({'place_id':place_id})
    for s in shouts:
        yield Shout.classify(s)

@classmethod
def classify(cls,shout):
    if shout.type == 1:
        return Type1(dict(shout))
    elif shout.type == 2:
        return Type2(dict(shout))
    elif shout.type == 3:
        return Type3(dict(shout))

问题是,这不会保留封装在默认pymongo Cursor中的括号密钥访问的原始方法。

如果我要创建一个只接受游标实例并包装其方法的包装类,我需要覆盖哪些魔术方法来保留原始游标的迭代行为?我在想这样的事情:

class StuffCursor():

    cursor = False

    def __init__(self,cursor):
        self.cursor = cursor

    def __getattr__(self,attr):
        #This passes off most key-based calls, like bracket-access, to the cursor
        return getattr(self.cursor,attr)

这正是我能想到的,但是任何可以在迭代器上堆叠一些额外处理,然后返回修改后的迭代器的东西都可以工作。

2 个答案:

答案 0 :(得分:1)

讨厌回答我自己的问题,但这可能对任何人用谷歌搜索都有用。这就是我最终的目标:

class ShoutCursor():
    """
    Custom cursor class that will instantiate shout models
    at query time.
    """

    cursor = False

    def __init__(self,cursor):
        self.cursor = cursor
        self.cursor.sort('created',DESCENDING)

    def __iter__(self):
        for ent in self.cursor:
            yield Shout.classify(ent)

    def __len__(self):
        return self.cursor.count()

    def __getitem__(self,index):
        try:
            ents = itertools.islice(self.cursor,index,index+1)
            return [Shout.classify(ent) for ent in ents][0]
        except TypeError:
            return list(Shout.classify(ent) for ent in itertools.islice(self.cursor,index.start,index.stop,index.step))

    def sort(*args,**kwargs):
        self.cursor.sort(*args,**kwargs)

    def __getattr__(self,attr):
        return getattr(self.cursor,attr)

__iter____getiem__方法是将已修改的实例化模型加载到生成器中的方法。其他方法保留本机游标操作。将光标传递给__init__设置功能,因为从mongo获取对象时,可以执行特殊的每次迭代逻辑。

答案 1 :(得分:0)

我不知道是否可以使用“猴子补丁”来实现这一点,但我最终做到了如下所示。这允许我在 Cursor 类上添加/覆盖函数。

from pymongo import MongoClient
from pymongo.collection import Collection
from pymongo.cursor import Cursor
from pymongo.database import Database

class CustomCursor(Cursor):
    # Customize Cursor here, either by overriding or extending with more methods


class CustomCollection(Collection):
    def __init__(self, database: Database, name: str) -> None:
        super().__init__(database, name)

    # find_one seems to use find, but you may need to override in more places.
    def find(self, *args, **kwargs):
        return CustomCursor(self, *args, **kwargs)


class CustomDatabase(Database):
    def __init__(self, client: MongoClient, name: str) -> None:
        super().__init__(client, name)

    def __getitem__(self, name: str) -> CustomCollection:
        return CustomCollection(self, name)

# usage:
client = MongoClient('127.0.0.1', 27017)
database = CustomDatabase(client, 'mydb')

我定义了一个 Serializable 基类,带有 from_dictto_dict 方法。这允许我在 to_list() 上定义一个 CustomCursor,以便我可以查询并获取类实例:

result: List[School] = database.schools.find().to_list(School)