使用具有“通用”字段的模型来建模继承

时间:2012-09-19 12:44:26

标签: database django django-models

我目前正在开玩笑如何使用Django建模。基本情况是我有一个对象应该作为chassis并提供一些sockets。然后,在modules的{​​{1}}上放置了大量不同的sockets。我想在Django中将这些不同的chassis建模为不同的类,但在数据库层使用公共类,可能涉及一些通用字段。

因此,数据库模型可能如下所示:

modules

当然,我不想用这种非规范化来破坏我的逻辑。这就是为什么我暗示某些应该使用Module表的class Module(models.model): name = models.CharField(max_length=128) # Or is there a better way to annotate a type? type = models.CharField(max_length=128) string1 = models.CharField(max_length=128) string2 = models.CharField(max_length=128) ... int1 = models.IntegerField() # Some kind of engine class that derives from Module # but does nothing else then "mapping" the generic # fields to something sensible class Socket(models.Model): is_on = models.ForeignKey(Chassis) name = models.CharField(max_length=128) type = models.CharField(max_length=128) class Connection(models.Model): chassis = models.ForeignKey(Chassis) module = models.ForeignKey(Module) via = models.ForeignKey(Socket) class Chassis(models.Model): name = models.CharField(max_length=128) modules= models.ManyToManyField(Module, through='Connection') class Group(models.Model): name = models.CharField(max_length=128) 类,但提供“逻辑”getter和setter,有效地将数据映射为“Horsepower”到“int1”。

所以我的问题基本上是:

  • 我对Django的合理性是什么?或者有更好的(可能是内置的)方式来解决这个问题吗?
  • 是否可以构造正确的类型,为非规范化模型提供包装方法的类型,具体取决于engine字段?

2 个答案:

答案 0 :(得分:1)

  

我对Django的合理性是什么?

一般的想法是可以的,但是非规范化可能会使您的查询不是最佳的。标准解决方案是为每种类型的模块创建子类Module;这将创建一个module表加上每个模块类型的表,其中包含特定于类型的内容。当然,这假设您不会在运行时创建或删除模块类型。

也就是说,您的模型存在一些问题:

class Module(models.model):
    name = models.CharField(max_length=128)
    # Or is there a better way to annotate a type?
    type = models.CharField(max_length=128) 
    string1 = models.CharField(max_length=128)
    string2 = models.CharField(max_length=128)
    ...
    int1 = models.IntegerField()

通常,type会被规范化以节省空间:

class ModuleType(models.model):
    name = models.CharField(max_length=128)
    # Any other type-specific parameters.

class Module(models.model):
    name = models.CharField(max_length=128)
    type = models.ForeignKey(ModuleType, related_name="modules")
    string1 = models.CharField(max_length=128)
    string2 = models.CharField(max_length=128)
    ...
    int1 = models.IntegerField()

class Socket(models.Model):
    is_on = models.ForeignKey(Chassis)
    name = models.CharField(max_length=128)
    type = models.CharField(max_length=128)

class Connection(models.Model):
    chassis = models.ForeignKey(Chassis)
    module = models.ForeignKey(Module)
    via = models.ForeignKey(Socket)

class Chassis(models.Model):
    name = models.CharField(max_length=128)
    sockets = m
    modules= models.ManyToManyField(Model, through='Socket')

Chassis一团糟。你没有定义sockets,写了Model,你可能想要module,而through可能应该引用Connectionthrough模型必须在链接的两端都有ForeignKey个。但是根据你的描述,我得到的更简单:

class Socket(models.Model):
    chassis = models.ForeignKey(Chassis, related_name="sockets")
    name = models.CharField(max_length=128)
    # Is `type` a ModuleType? If so, use a ForeignKey.
    # If not, create a SocketType model.
    type = models.___
    module = models.ForeignKey(Module, related_name="sockets")

class Chassis(models.Model):
    name = models.CharField(max_length=128)
    sockets = models.IntegerField()
    modules = models.ManyToManyField(Socket)

通过更好地描述您的建模内容,可以进一步细化。例如,我不确定ManyToMany是你想要的。您可能需要从该机箱的实例拆分机箱的设计(即给定类型机箱的所有实例通用的东西,包括其套接字)(将参考设计,并有另一个表映射套接字到模块)。


  

是否可以自动构建正确的类型,具体取决于Module.type字段?

那是工厂设计模式。在Python中,您将其实现为构造函数的字典:

class Module(models.model):
    # ...
    CONSTRUCTORS = {}

    @staticmethod
    def register_constructor(type_name, constructor):
        Module.CONSTRUCTORS[type_name] = constructor

    def construct(self):
        return Module.CONSTRUCTORS[self.type.name](self)

我认为您不需要特定的引擎类;各种模块类就足够了。

答案 1 :(得分:1)

为了定义抽象基类,您可以执行以下操作:

class Module(models.model):
    name = models.CharField(max_length=128)
    type = models.CharField(max_length=128) 
    string1 = models.CharField(max_length=128)
    string2 = models.CharField(max_length=128)
    ...
    int1 = models.IntegerField()

    class Meta:
        abstract = True

class SpecificModule(Module):
     subClassField = models.CharField(max_length=128)

我建议阅读文档的this part,因为它是处理继承和抽象类的一个非常好的起点。

您还可以定义不带class Meta: abstract = True的父模块类。唯一的区别是abstract = TrueSpecificModule的所有字段和抽象模块父类的所有字段都将在子类表中为特定模块创建,而没有{{1}定义类的表将创建模块,包含Module表中可用的所有“通用”字段,以及表中特定于模块类的所有子类特定字段。

编辑:回答有关父母与子女之间关系的问题。

由于在子类表(docs)中创建了隐式的一对一字段,您可以使用此查询获取子对象。

abstract = True

我认为Django也应该创建一个默认的related_name,但不确定它是哪一个。

请注意,仅在#get the parent parent = Module.objects.get(name="my first module") #this is possible print parent.name >>> "my first module" #this is not possible print parent.subClassField >>> Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'Module' object has no attribute 'subClassField' #get the corresponding child child = SpecificModule.objects.get(module_ptr_id=parent.pk) #finally we can print the value print child.subClassField 情况下创建从父级到子级的隐式一对一关系。然而,如果abstract=False您不会将抽象父作为对象,那么它是抽象的..