Django动态模型的Python重载

2024-09-29 05:18:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我有两个相关的模型:

class FirstModel(models.Model):
    base_value = models.FloatField()

class SecondModel(models.Model):
    parent = models.ForeignKey(FirstModel)

    @property
    def parent_value(self):
        return self.parent.base_value

    @property
    def calculate(self):
        return self.parent_value + 1

一般来说,SecondModel.calculate主要用于与其相关的FirstModel的上下文中。但是,有时我希望能够用一个临时值作为其parent_value调用calculate。像这样:

^{pr2}$

显然不能这样做,因为parent_value是只读属性。我也有许多不同的模型类似于SecondModel,需要有这种能力。在

我考虑并尝试了几件事,但似乎都不太管用:

1)编写Django代理模型是可能的,但是对象的数量相当多,所以我会编写很多类似的代码。另外,似乎还有一个与重写属性相关的bug:https://code.djangoproject.com/ticket/16176。但看起来像这样:

class ModelProxy(SecondModel):
    class Meta:
         proxy = True
    def __init__(self, temp_value):
         self.parent_value = temp_value

2)重载实例上的parent_value属性,如下所示:

foo = SecondModel()
setattr(foo, 'parent_value', 10)

但是不能这样做,因为属性是类的成员,而不是实例。我只希望为实例设置临时值

3)元类还是类生成器?似乎过于复杂。另外,我不确定如果我使用元类动态生成属于模型。模型. 我会遇到数据库表不同步的问题吗?在

4)使用适当的getter和setter重写属性?-也许解决方案是重写SecondModel以便设置属性?在

有什么建议吗?在


Tags: 实例模型selfbasemodel属性valuemodels
3条回答

我相信mixin可以实现您想要的功能,并提供一种简单且可重用的方法来支持计算中的临时值。通过将下面的示例混合到您想要的每个模型中,您可以:

  • 为每个模型设置临时父值
  • 调用calculate时,它将检查是否有可用的属性父值,如果没有,它将在计算中使用临时父值。在

下面的代码应该达到你想要的-抱歉,我还没能测试它,但它应该是正确的-请让我知道如果有任何问题需要编辑。在

class CalculateMixin(object):

    @property
    def temp_parent_value(self):
        return self._temp_parent_value

    @temp_parent_value.setter
    def temp_parent_value(self, value):
        self._temp_parent_value = value

    @property
    def calculate(self):
        parent_value = self.parent_value if self.parent_value else self.temp_parent_value
        return parent_value + 1


class SecondModel(models.Model, CalculateMixin):
    parent = models.ForeignKey(FirstModel)

    self.temp_parent_value = 'Whatever value you desire'

    @property
    def parent_value(self):
        return self.parent.base_value

可以使用属性设置器:

class SecondModel(models.Model):
    _base_value = None

    parent = models.ForeignKey(FirstModel)

    @property
    def parent_value(self):
        if self._base_value is None:
            return self.parent.base_value
        else:
            return self._base_value

    @parent_value.setter
    def parent_value(self, value):
        self._base_value = value

    @property
    def calculate(self):
        return self.parent_value + 1

我认为您可以使用下面显示的mixin PropertyOverrideMixin来完成所需的操作,如果某些属性值不可用,那么它将查找前缀为temp_的相同属性。这将允许您提供临时值,当无法查找实际属性值时可以使用这些值。在

下面是mixin,一些示例模型和一个单元测试来展示它是如何工作的。希望这能适应你的问题!最后值得一提的是,这里的属性可以与普通的对象属性互换,并且仍然可以正常工作。在

from unittest import TestCase


class PropertyOverrideMixin(object):

    def __getattribute__(self, name):
        """
        Override that, if an attribute isn't found on the object, then it instead
        looks for the same attribute prefixed with 'temp_' and tries to return
        that value.
        """

        try:
            return object.__getattribute__(self, name)
        except AttributeError:
            temp_name = 'temp_{0}'.format(name)
            return object.__getattribute__(self, temp_name)


class ParentModel(object):

    attribute_1 = 'parent value 1'


class Model(PropertyOverrideMixin):

    # Set our temporary property values
    @property
    def temp_attribute_1(self):
        return 'temporary value 1'

    @property
    def temp_attribute_2(self):
        return 'temporary value 2'

    # Attribute 1 looks up value on its parent
    @property
    def attribute_1(self):
        return self.parent.attribute_1

    # Attribute 2 looks up a value on this object
    @property
    def attribute_2(self):
        return self.some_other_attribute


class PropertyOverrideMixinTest(TestCase):

    def test_attributes(self):
        model = Model()

        # Looking up attributes 1 and 2 returns the temp versions at first
        self.assertEquals('temporary value 1', model.attribute_1)
        self.assertEquals('temporary value 2', model.attribute_2)

        # Now we set the parent, and lookup of attribute 1 works on the parent
        model.parent = ParentModel()
        self.assertEquals('parent value 1', model.attribute_1)

        # now we set attribute_2, so this gets returned and the temporary ignored
        model.some_other_attribute = 'value 2'
        self.assertEquals('value 2', model.attribute_2)

相关问题 更多 >