【问题标题】:Creating instances of Django proxy models from their base class从基类创建 Django 代理模型的实例
【发布时间】:2013-08-27 19:26:21
【问题描述】:

我有一系列看起来像这样的模型:

class Analysis(models.Model):
    analysis_type = models.CharField(max_length=255)

    def important_method(self):
        ...do stuff...


class SpecialAnalysis(Analysis):
    class Meta:
        proxy = True

    def important_method(self):
        ...something different...

这都是非常标准的。但是,我想做的是根据analysis_type 字段的值自动将Analysis 模型转换为代理模型。例如,我希望能够编写如下所示的代码:

>>> analysis = Analysis.objects.create(analysis_type="nothing_special")
>>> analysis.__class__
<class 'my_app.models.Analysis'>

>>> analysis = Analysis.objects.create(analysis_type="special")
>>> analysis.__class__
<class 'my_app.models.SpecialAnalysis'>

>>> analysis = Analysis.objects.get(pk=2)
>>> analysis.__class__
<class 'my_app.models.SpecialAnalysis'>

>>> # calls the ``important_method`` of the correct model
>>> for analysis in Analysis.objects.all():
...     analysis.important_method()

这甚至是遥不可及的吗?一个类似的问题被问到here,它实际上为迭代示例提供了一些代码,但它仍然给我留下了如何从其父类获取或创建代理类实例的问题。我想我可以重写一堆管理器方法,但我觉得必须有一种更优雅的方式来做到这一点。

【问题讨论】:

    标签: python django


    【解决方案1】:

    我还没有找到一种“干净”或“优雅”的方式来做到这一点。当我遇到这个问题时,我通过欺骗 Python 来解决它。

    class Check(models.Model):
        check_type = models.CharField(max_length=10, editable=False)
        type = models.CharField(max_length=10, null=True, choices=TYPES)
        method = models.CharField(max_length=25, choices=METHODS)
        'More fields.'
    
        def __init__(self, *args, **kwargs):
            super(Check, self).__init__(*args, **kwargs)
            if self.check_type:
                map = {'TypeA': Check_A,
                    'TypeB': Check_B,
                    'TypeC': Check_C}
                self.__class__ = map.get(self.check_type, Check)
    
        def run(self):
            'Do the normal stuff'
            pass
    
    
    class Check_A(Check):
        class Meta:
            proxy = True
    
        def run(self):
            'Do something different'
            pass
    
    class Check_B(Check):
        class Meta:
            proxy = True
    
        def run(self):
            'Do something different'
            pass
    
    
    class Check_C(Check):
        class Meta:
            proxy = True
    
        def run(self):
            'Do something different'
            pass
    

    它不是很干净,但它是解决我问题的最简单的破解方法。

    也许这对你有帮助,也许没有。

    我也希望其他人有一个更 Pythonic 的解决方案来解决这个问题,因为我正在计算直到这个方法失败并回来困扰我的天数..

    【讨论】:

    • 嗯,这绝对比我尝试的疯狂的东西更干净。感谢您的帮助!
    • 您的方法本身不太可能失败,但手动映射肯定不能很好地扩展;未来的访问者会更好地使用 Model.__subclasses__() 方法来获取代理模型列表并根据需要动态查询其中的部分/全部。
    【解决方案2】:

    这是一个很好的方法,我并不特别认为它是一种作弊。恕我直言,这是对 __init__ 函数的一些增强,因此当您添加更多类时不必更改它。

    def __init__(self, *args, **kwargs):
        super(Analysis, self).__init__(*args, **kwargs)
        if not self.__type and type(self) == Analysis:
            raise Exception("We should never create a base Analysis object. Please create a child proxy class instead.")
    
        for _class in Analysis.__subclasses__():
            if self.check_type == _class.__name__:
                self.__class__ = _class
                break
    
    def save(self, **kwargs):
        self.check_type = self.__class__.__name__
        super(Analysis, self).save(**kwargs)
    

    希望这会有所帮助!

    【讨论】:

    • 哦,太好了。不知道__subclasses__,肯定会提高可维护性。
    猜你喜欢
    • 2012-12-11
    • 2023-03-28
    • 1970-01-01
    • 2023-02-08
    • 2021-02-19
    • 1970-01-01
    • 1970-01-01
    • 2013-02-11
    • 1970-01-01
    相关资源
    最近更新 更多