【问题标题】:python SubClasses from a Class that inherits from models.Model从继承自models.Model的类的python子类
【发布时间】:2013-02-07 15:55:14
【问题描述】:

我这里有些疑惑……

假设我有 3 个班级:

class CarSpec(models.Model):
    x = models.IntegerField(default=20)
    y = models.CharField(max_length=100, blank=True)
    z = models.CharField(max_length=50, blank=True)
    chassis = models.ForeignKey(Chassis, unique=True, limit_choices_to={'type':'A'})
    car_brand = models.CharField(max_length=100, blank=True)
    car_model = models.CharField(max_length=50, blank=True)
    number_of_doors = models.IntegerField(default=2)

class MotoSpec(models.Model):
    x = models.IntegerField(default=20)
    y = models.CharField(max_length=100, blank=True)
    z = models.CharField(max_length=50, blank=True)
    chassis = models.ForeignKey(Chassis, unique=True, limit_choices_to={'type':'C'})
    motor_brand = models.CharField(max_length=100, blank=True)
    motor_model = models.CharField(max_length=50, blank=True)
    powered_weels = models.IntegerField(default=1)

class Chassis(models.Model):
    name = models.CharField(max_length=50, blank=False)
    type = models.CharField(max_length=2, choices = GAME_TYPES, default="A")

GAME_TYPES = (('A', 'Car'),('B', 'Truck'),('C', 'Motorcycle'))

我正在使用这 3 个类,但在我的应用程序中,我必须一直检查 chassis 类型,以便将一些业务规则应用于每种情况...... 我认为这不是正确的方法.. 所以我计划了这个:

class Spec(models.Model):
    x = models.IntegerField(default=20)
    y = models.CharField(max_length=100, blank=True)
    z = models.CharField(max_length=50, blank=True)

    class Meta:
        abstract = True

并且有两个子类:

class CarSpec(Spec):
    chassis = models.ForeignKey(Chassis, unique=True, limit_choices_to={'type':'A'})
    car_brand = models.CharField(max_length=100, blank=True)
    car_model = models.CharField(max_length=50, blank=True)
    number_of_doors = models.IntegerField(default=2)

class MotoSpec(Spec):
    chassis = models.ForeignKey(Chassis, unique=True, limit_choices_to={'type':'C'})
    motor_brand = models.CharField(max_length=100, blank=True)
    motor_model = models.CharField(max_length=50, blank=True)
    powered_weels = models.IntegerField(default=1)

class Chassis(models.Model):
    name = models.CharField(max_length=50, blank=False)
    type = models.CharField(max_length=2, choices = GAME_TYPES, default="A")

GAME_TYPES = (('A', 'Car'),('B', 'Truck'),('C', 'Motorcycle'))

好的,直到这里一切正常,我的应用程序中没有任何改变,这些应用程序与以前的类一起工作,并且所有对象都按预期很好地保存在数据库中..

但是,我的问题仍然存在..因为我继续实例化 CarSpec 和 MotoSpec 而不是 Spec...但是...我想一直使用 Spec 而不是扩展类...这样,我能做什么为了能够实例化一个 Spec 对象,将 Chassis 传递给他的 init 方法,以便从该(或其他)方法获取 CarSpec 或 MotoSpec..

EDITED-IMPORTANT:我为 MotoSpec 添加了 powered_weels 属性,并为 CarSpec 添加了 number_of_doors 属性 因为我对两个 Specs 中的每一个都有一些特定的字段

再次编辑:在我看来,我希望避免每次在处理 Specs 时都进行类型验证,并将其留给所涉及的类之一。继续,我希望能够添加一个新的 Spec 对象,而不必担心更改我的视图.. 只有与 Specs 相关的对象..

# CarSpec
if game_type == "A":
    stuff = CarSpec.restore_state(request, game_session)
# MotoSpec
elif game_type == "C":
    stuff = MotoSpec.restore_state(request, game_session)

已编辑:我在我的 Spec 类中添加了一个 restore_state,但后来我想我发现了一些与循环导入相关的问题.. OMG.. 这让我很生气。我有 .NET 背景,python 在这些方面对我来说并不容易:S

【问题讨论】:

    标签: python django class subclass abstract


    【解决方案1】:

    在 Spec 类中添加底盘、品牌和型号属性,然后对 CarSpec 和 MotoSpec 类使用 proxy models,可能添加 car_brand()、motor_brand() 等方法...

    【讨论】:

    • 谢谢,但我添加了一些不同的属性(就像我在真实情况下一样)..
    • 如何将额外的字段设为可选,并在需要时使用代理隐藏/要求它们。
    【解决方案2】:

    提议:

    class Spec(models.Model):
        x = models.IntegerField(default=20)
        y = models.CharField(max_length=100, blank=True)
        z = models.CharField(max_length=50, blank=True)
        brand = models.CharField(max_length=100, blank=True)
        model = models.CharField(max_length=50, blank=True)
    
    
    class CarSpec(Spec):
        chassis = models.ForeignKey(Chassis, unique=True, limit_choices_to={'type':'A'})
        number_of_doors = models.IntegerField(default=2)
    CarSpec._meta.get_field('brand').verbose_name = 'Car Brand'
    CarSpec._meta.get_field('model').verbose_name = 'Car Model'
    
    
    class MotoSpec(Spec):
        chassis = models.ForeignKey(Chassis, unique=True, limit_choices_to={'type':'C'})
        powered_weels = models.IntegerField(default=1)
    MotoSpec._meta.get_field('brand').verbose_name = 'Motor Brand'
    MotoSpec._meta.get_field('model').verbose_name = 'Motor Model'
    
    
    class Chassis(models.Model):
        GAME_TYPES = (
            ('A', 'Car'),
            ('B', 'Truck'),
            ('C', 'Motorcycle')
        )
    
        name = models.CharField(max_length=50, blank=False)
        type = models.CharField(max_length=2, choices = GAME_TYPES, default="A")
    

    或者您可以在 forms.py 中输入详细名称

    class CarSpec(Spec):
        chassis = models.ForeignKey(Chassis, unique=True, limit_choices_to={'type':'A'})
        number_of_doors = models.IntegerField(default=2)
    
    
    class MotoSpec(Spec):
        chassis = models.ForeignKey(Chassis, unique=True, limit_choices_to={'type':'C'})
        powered_weels = models.IntegerField(default=1)
    

    【讨论】:

    • 但我不想有两张桌子(一张用于规格,另一张用于汽车/摩托)。另一方面,这将让我在 Spec 超类上定义和实现一些方法......无论如何我可以拥有两个世界中最好的?
    • 是的,您可以通过将 Spec 转换为 Abstract 来修改我的答案
    • 那个,还是有问题,从一开始我就主要关心这一切
    • 那(这是我解决问题的第一个方法)是否仍然存在问题,这从一开始就是我对这一切的主要关注:想象一下 CarSpec 和 MotoSpec 有一个名为 doStuff 的(静态)方法(x,y) 在我的一个观点中,我喜欢: if game_type == "A": stuff = CarSpec.restore_state(request, game_session) elif game_type == "C": stuff = MotoPlay.restore_state(request, game_session) 并且我想从我的视图中去掉那种类型验证......如果我有一个对象方法而不是静态方法,这同样适用
    【解决方案3】:

    尽管您可以删除 Spec 类上的 class Meta(这样您就有了 Spec 对象)。我不认为你可以覆盖类上的__init__ 方法来实例化CarSpecMotoSpec 对象。这会给你一个循环依赖; CarSpec 对象的定义依赖于Spec 对象的定义(因为CarSpecSpec 的后代)。您不能使 Spec 对象的定义依赖于 CarSpec 对象(如果 CarSpec 出现在 Spec__init__ 定义中,就会出现这种情况)。

    我个人认为您应该使用 lysergia25 建议的代理模型,并隐藏/要求代理中的其他字段。

    更新

    假设您将所有字段添加到Spec(在仅出现在一个子类中的字段上添加blank=True, null=True),那么您可以执行以下操作 -

    class MotoSpec(Spec):
        class Meta:
            proxy = True
    
        def __init__(self, *args, **kwargs):
            super(MotoSpec, self).__init__(*args, **kwargs)
            self.fields['number_of_doors'].editable = False
            self.feilds['powered_wheels'].blank = False
    

    这应该对所有表单(inc admin)隐藏字段number_of_doors,同时需要powered_wheels

    【讨论】:

    • 这不是一个好方法吗:我将所有 Spec 属性传递回子类,使用 Spec 作为抽象而不使用任何字段,然后从那里开始......因为我已经已经有数千行的 CarSpec 和 MotoSpec 表..
    • 你是什么意思:“并隐藏/需要代理中的其他字段”。
    • 谢谢。但是……而且……反过来呢,我的意思是,如果我实现两个单独的表(就像我目前拥有的 CarSpec 和 MotoSpec)并且我有一个 Spec 代理类来操作和调用多个方法怎么办来自 CarSpec 和 MotoSpec。说实话,我首先真正感兴趣的是:有一种方法,在我看来,我不必检查它是什么 Spec 类型,并在内部使用的一个类中进行。这样我就可以随时拥有一个独立的规范,并且避免改变我的观点。我只是似乎没有从我的脑海中得到它。
    • 好的,不可能:"基类限制¶代理模型必须从一个非抽象模型类继承。您不能从多个非抽象模型继承,因为代理模型没有提供不同数据库表中的行之间的任何连接。代理模型可以从任意数量的抽象模型类继承,前提是它们不定义任何模型字段。"
    • 使用基类和 2 个代理模型。在基类上添加一个字段,告诉您您的 db 元组代表哪个子类。然后通过覆盖代理类中的 save() 自动填充该字段。
    【解决方案4】:

    您应该使用多表继承,如 django documentation 中所述

    【讨论】:

    • 对不起,你能说得更具体点吗... multitable,这不是我做的吗?
    • 哦,对不起,我的方法是抽象父类。但是,即便如此,你能在这里给我一些帮助吗..
    • 这个问题和我一样吗? stackoverflow.com/questions/1712683/…
    猜你喜欢
    • 2020-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多