从继承自models.Model的类中创建Python子类
我这里有一些疑问...
假设我有三个类:
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'))
我一直在使用这三个类,但在我的应用中,我需要不断检查底盘类型,以便根据不同情况应用一些业务规则……我觉得这样做不太对,所以我计划了这个:
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 对象,并在它的初始化方法中传入一个底盘,以便从这个(或其他)方法中得到 CarSpec 或 MotoSpec 呢……
编辑-重要:我为 MotoSpec 添加了 powered_weels 属性,为 CarSpec 添加了 number_of_doors 属性,因为这两个规格各自有一些特定的字段。
编辑-再次:在我的视图中,我想避免每次处理规格时都进行类型验证,而是把这个工作留给相关的类。总之,我希望能够添加一个新的 Spec 对象,而不必担心更改我的视图……只需要关注与规格相关的对象……
# 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 方法,但我发现了一些与循环导入相关的问题……天哪,这让我很头疼。我有 .NET 的背景,而 Python 在这方面对我来说并不容易 :S
4 个回答
建议:
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)
虽然你可以去掉class Meta
这个部分,这样就能得到Spec
对象。但我觉得你不能在这个类里重写__init__
方法来创建CarSpec
或MotoSpec
对象。这样会导致循环依赖的问题;因为CarSpec
对象的定义依赖于Spec
对象的定义(因为CarSpec
是Spec
的子类)。你不能让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
这样应该能在所有表单(包括管理员界面)中隐藏number_of_doors
这个字段,同时要求powered_wheels
字段。
给Spec类添加底盘、品牌和型号这几个属性,然后对CarSpec和MotoSpec类使用代理模型,也许还可以添加一些方法,比如car_brand()、motor_brand()等等...