使用MongoEngine的Python - 访问自定义模型属性列表

2 投票
1 回答
1185 浏览
提问于 2025-04-17 16:39

我想方便地访问我一些Python模型类中的自定义模型属性。我在使用MongoEngine作为我的对象关系映射(ORM),不过这个问题其实是关于继承和面向对象编程的一般问题。

具体来说,我希望能够从一个混入类(Mixin class)中的方法访问自定义模型属性,而这个混入类我会在所有模型类中继承。

考虑以下的类结构:

class ModelMixin(object):
    def get_copy(self):
        """
        I'd like this to return a model object with only the custom fields
        copied.  For the City object below, it would run code equivalent to:

          city_copy = City()
          city_copy.name = self.name
          city_copy.state = self.state
          city_copy.popluation = self.population
          return city_copy

        """


class City(BaseModel, ModelMixin):
    name = orm_library.StringField()
    state = orm_library.StringField()
    population = orm_library.IntField()

这样就可以实现以下功能:

>>> new_york = City(name="New York", state="NY", population="13000000")
>>> new_york_copy = new_york.get_copy()

但是,它必须适用于任意模型。也就是说,它需要能够判断子类中定义了哪些自定义属性,实例化那个子类的一个实例,并且只复制那些自定义属性,而不复制父类BaseModel中的内置属性和方法(因为里面有很多我不关心的随机东西)

有没有人知道我该怎么做呢?

1 个回答

1

我觉得你有几个工具可以用来实现这个目标(如果我下面的代码不完全符合你的需求,你应该能很快调整它)。具体来说:

  • __class__ 属性可以让你知道一个对象属于哪个类。
  • vars() 函数可以列出一个对象(或类)的所有属性。
  • setattr()getattr() 函数可以让你通过名字来操作任意属性。
  • 为了区分哪些类属性对你来说是重要的(在你的情况下,就是那些 MongoEngine 字段),我会检查模型属性本身的类类型;快速查看 MongoEngine 的源代码似乎表明,你需要把它改成 'BaseField'

class ModelMixin(object):
    def get_copy(self):

        # Get the class for the 

        C = self.__class__

        # make a new copy

        result = C()

        # iterate over all the class attributes of C
        # that are instances of BaseField

        for attr in [k for k,v in vars(C).items() if v.__class__ == BaseField]:
            setattr(result, attr, getattr(self, attr))

        return result

为了测试上面的内容(创建 MongoEngine 模型/字段的虚拟类)

class BaseField(object):
    pass

class BaseModel(object):
    baseField = BaseField()

class City(BaseModel, ModelMixin):
    x = BaseField()
    y = BaseField()

c = City()
c.x = 3
c.y = 4
c.baseField = 5

d = c.get_copy()
print d.x # prints '3'
print d.y # prints '4'
print d.baseField # correctly prints main.BaseField, because it's not set for d

撰写回答