包装课程'子类方法

时间:2017-07-24 18:48:16

标签: python mongodb class generics

我的主要课程定义如下:

from pymongo import MongoClient
from credentials import MongoMarketPlaceAuth


client = MongoClient('mongodb://conn_url:conn_port/', ssl=True)
db_connection = client.DB_NAME
db_connection.authenticate(name=MongoMarketPlaceAuth.name, password=MongoMarketPlaceAuth.password)
MarketPlace = db_connection


class MarketPlaceObjects(object):

    def __init__(self, collection, fields={}, default_filter={}):
        self.collection_name = collection
        self.fields = fields
        self.default_filter = default_filter
        if MarketPlace[collection].count() == 0:
            raise Exception('Collection does not exist or has zero objects')
        self.objects = MarketPlace[collection]

然后我有一个继承我的主类的类:

class Products(MarketPlaceObjects):

    def __init__(self):
        super(Products, self).__init__(collection='FlatProduct')

如果这样使用:

from products import Products
p = Products()
p.objects.find_one()

返回描述产品所有方面的字典。 我想做的是弄清楚如何

p.objects.find_one()

它不是返回字典或字典列表,而是返回一个Product对象(来自单个返回的字典)或Product对象列表(来自返回的字典列表)。

我很难过,因为我不确定如何使用自己的Product类包装PyMongo集合类的find_one()或find()方法。

更新(2017-07-25): 这就是我最终做的事情。仍然需要一些优化:

市场的通用类:

class CollectionObjectInstance(object):
    def __init__(self, response):
        for key, value in response.items():
            if isinstance(value, dict):
                self.__dict__[key] = CollectionObjectInstance(value)
            else:
                self.__dict__[key] = value


class CollectionObjectsManager(object):

    serializer = CollectionObjectInstance
    collection = None
    default_projection = {}
    default_filter = {}

    def __init__(self, **kwargs):
        self.__dict__ = kwargs

    def find(self, filter={}, projection={}):
        filter = self.default_filter.update(filter)
        projection = self.default_projection.update(projection)
        res = self.collection.find(filter, projection)
        for o in res:
            yield self.serializer(o)

    def find_one(self, filter={}, projection={}):
        filter = self.default_filter.update(filter)
        projection = self.default_projection.update(projection)
        res = self.collection.find_one(filter, projection)
        return self.serializer(res)


class MarketPlaceCollection(object):

    collection_name = None
    serializer = None
    objects_manager = CollectionObjectsManager

    def __init__(self, *args, **kwargs):
        if self.collection_name is None:
            raise Exception('collection_name must be defined in class')
        if self.serializer is None:
            raise Exception('serializer must be defined in class')
        collection = MarketPlace[self.collection_name]
        if collection.count() == 0:
            raise Exception('Collection does not exist or has zero objects')
        self.collection = collection
        self.objects = self.objects_manager(**self.__dict__, **self.__class__.__dict__)

使用继承的产品实现:

from marketplace import MarketPlaceCollection, CollectionObjectInstance
from settings import BASE_URL


URL_SUFFIX = 'product/'
CASH_PRICE_FEE = 50
LEASE_TERM_WEEKS = 52


class Product(CollectionObjectInstance):

    def url(self):
        url = BASE_URL + URL_SUFFIX + self.slug
        return url


class Products(MarketPlaceCollection):

    collection_name = 'FlatProduct'
    serializer = Product

1 个答案:

答案 0 :(得分:1)

当您致电p.objects时,您会收到集合列表本身,该行列在self.objects = MarketPlace[collection]行上。您的Products无法控制.objects属性中的方法或属性 - 它是pymongo返回的对象。

因此,要控制Products.objects的方法和属性,必须使用所需的方法创建另一个类,并在尝试检索Products.objects时返回该类的对象。 / p>

虽然Python具有"属性"装饰器和描述符协议,以及objects属性的更复杂的自动化可以使用它们,在这种情况下,它可以以非常简单的方式进行。只需拥有另一个接收集合的类,并通过在其中实现__getattr__将其他属性代理到集合:

class ObjectList(object):
    def __init__(self, collection, cls):
        self.collection = collection
        self.cls = cls

    def find_one(self, *args, **kw):
        raw_list = self.collection.find_one(*arg, **kw)
        objects = [self.cls(**obj) for obj in raw_list]
        return objects[0] if len(objects) == 1 else objects

    def __getattr__(self, attr):
        """this is called automatically by Python when a
           normal attribute is not found  in this object
        """ 
        return getattr(self.collection, attr)


class MarketPlaceObjects(object):

    def __init__(self, collection, cls, fields=None, default_filter=None):
        self.collection_name = collection
        self.fields = fields if fields else {}
        self.default_filter = default_filter if defaut_filter else {}
        if MarketPlace[collection].count() == 0:
            raise Exception('Collection does not exist or has zero objects')
        self.objects = ObjectList(MarketPlace[collection], cls)

class Products(MarketPlaceObjects):

    def __init__(self):
        super(Products, self).__init__(collection='FlatProduct', cls=Product)