2017-07-24 55 views
0

我的主要类被定义为如下:包装纸一类子类方法

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类来包装PyMongo集合类的find_one()或find()方法,所以我很困难。


更新(2017年7月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

除了你的问题,还有就是在你的代码的Python begginers一个常见的错误:你不应该使用像{}}这样的可变对象作为函数或方法的默认参数。这是因为这些dictioanaries是在模块加载时创建的,并且在每次调用该函数或方法时都会重新使用。使用'None'作为默认值,并在方法体内使用'if'语句将参数分配给空dict。 – jsbueno

+0

真的!我试图避免ifs,因为我认为它的广告开销。你认为如果使用if,然后分配空字典,性能会更好? –

回答

1

当你调用p.objects你收集名单本身,就行了,可归因self.objects = MarketPlace[collection]。您的Products不再控制.objects属性中的方法或属性 - 它是由pymongo返回的对象。

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

尽管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) 
+0

谢谢!在我看到您的答案之前,我最终编写了一个解决方案。但是,我肯定会重写我的代码以采用您使用的一些技术。我的代码目前看起来非pythonic哈哈。 –