Python和Django中的继承与工厂函数
我正在创建一个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能够自动设置。我从这篇文章中了解到,自己写构造函数并不是个好主意,使用工厂函数会更好。所以现在我的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")
,然后就能得到一个合适的对象,里面的uuid
、objmodule
和objclass
字段都设置得很正确。然而,当我继续创建像ChildElement2
和ChildElement3
这样的类时,我发现我在每个地方都插入了完全相同的静态工厂函数。这让我觉得不舒服,因为代码重复是个坏习惯。
如果是普通的方法,我可以把create
工厂函数放到BaseElement
里,但在这里我不能这样做,因为我没有self的引用(因为它还没被创建),所以无法获取调用这个方法的对象的类的信息。
有没有办法把这个工厂函数迁移到BaseElement
类里,这样我就不需要到处重复这段代码,同时还能确保自动设置uuid
、objmodule
和objclass
的值呢?
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)
我用这个方法测试了一下,似乎达到了你想要的效果。我成功创建了一个“从Base继承”的对象,里面有你需要的字段,而且没有写很多重复的代码。
如果你把 create()
定义成一个 @classmethod
,而不是 @staticmethod
,那么你就可以使用类对象,这样就不需要直接用类名来引用它了:
@classmethod
def create(cls, *args, **kwargs):
obj = cls(*args, **kwargs)
obj.set_defaults()
return obj
这样做就变得通用,可以放在基类里,而不需要每个子类都写一遍。