【问题标题】:Inheritance and factory functions in Python and DjangoPython 和 Django 中的继承和工厂函数
【发布时间】:2009-12-11 21:32:35
【问题描述】:

我正在创建一个 Django 应用程序,它在其模型中使用了一些继承,主要是因为我需要为所有内容分配一个 UUID 和一个引用,以便我知道它是什么类。这是基类的简化版本:

class BaseElement(models.Model):
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4()))
    objmodule = models.CharField(max_length=255, editable=False, blank=False)
    objclass = models.CharField(max_length=255, editable=False, blank=False)

class ChildElement(BaseElement):
    somefield = models.CharField(max_length=255)

我想确保自动设置 objmodule、objclass 和 uuid。我从this post 了解到,通过编写自己的构造函数来做到这一点是个坏主意,而且我最好编写一个工厂函数。所以现在我的 BaseElement 和 ChildElement 看起来像这样:

class BaseElement(models.Model):
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4()))
    objmodule = models.CharField(max_length=255, editable=False, blank=False)
    objclass = models.CharField(max_length=255, editable=False, blank=False)

    def set_defaults(self):
        self.objmodule = unicode(self.__class__.__module__)
        self.objclass = unicode(self.__class__.__name__)
        self.uuid = unicode(uuid4())

class ChildElement(BaseElement):
    somefield = models.CharField(max_length=255)

    @staticmethod
    def create(*args, **kwargs):
        ce = ChildElement(*args, **kwargs)
        ce.set_defaults()
        return ce

这行得通。我可以调用ChildElement.create(somefield="foo"),我会得到一个适当的对象,其中uuidobjmoduleobjclass 字段设置正确。然而,当我创建更多类,如ChildElement2ChildElement3 时,我发现我正在插入完全相同的静态工厂函数。这让我很恼火,因为代码重复很糟糕。

使用普通方法,我只需在 BaseElement 中插入 create 工厂函数,但是,我不能在此处执行此操作,因为我没有 self 句柄(因为它尚未创建) 以获取有关调用该方法的对象的类的信息。

有没有办法可以将这个工厂迁移到BaseElement 类中,这样我就不必到处复制这段代码并且仍然拥有它,因此它会自动设置uuidobjmoduleobjclass?

【问题讨论】:

    标签: python django inheritance


    【解决方案1】:

    如果您将create() 设为@classmethod 而不是@staticmethod,您将可以访问类对象,您可以使用它而不是通过名称来引用它:

    @classmethod
    def create(cls, *args, **kwargs):
        obj = cls(*args, **kwargs)
        obj.set_defaults()
        return obj
    

    这现在是通用的,可以在基类上而不是每个子类上。

    【讨论】:

    • 这正是我想要的。谢谢你提醒我@classmethod。这不是我经常使用的东西。
    • 在 Python 中,@staticmethod 很少有很好的用例——如果有的话,你可能应该少用它。通常,如果某些东西不是 @classmethod,它更适合作为模块中的简单顶级函数。
    【解决方案2】:

    我认为您最好在 BaseElement 中覆盖 save 。然后在保存时您可以设置这些字段。它会是这样的:

    class MyBase(models.Model):
        uuid = models.CharField(max_length=64, editable=False, blank=True,
            default=lambda:unicode(uuid4()))
        objmodule = models.CharField(max_length=255, editable=False, blank=False)
        objclass = models.CharField(max_length=255, editable=False, blank=False)
    
        def save(self):
            if not self.id:
                self.objmodule = unicode(self.__class__.__module__)
                self.objclass = unicode(self.__class__.__name__)
                self.uuid = unicode(uuid4())
            super(self.__class__.__base__, self).save()
    
    class InheritedFromBase(MyBase):
        new_field = models.CharField(max_length=100)
    

    我对此进行了测试,它似乎可以满足您的需求。我能够创建一个“InheritedFromBase”对象,其中包含您需要的字段,而无需大量重复代码。

    【讨论】:

    • +1:简单地覆盖save 几乎总是正确的做法。
    • 这很接近,并且可能被证明是可以接受的,但不是我所需要的。现在我在服务器上创建了一些对象,然后将它们序列化到客户端以在 AJAXy 应用程序中使用。客户端可以修改属性然后保存对象。通过您的设置,在保存之前,它们不会得到分配 uuidobjmoduleobjclass。当我在客户端使用uuid 时,我可能需要向服务器添加一些额外的逻辑,或者让客户端分配它。如此接近,但我想找到一种不首先调用save 的方法。但是,如果这是正确的方法,我可以处理。
    猜你喜欢
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多