Django模型子类化类型声明及作品集模型构建咨询
Django模型子类化的类型字段处理方案
看起来你是想用**单表继承(STI)**来区分画廊对象和链接对象,不过你直接在子类里赋值oType = 0的写法是有问题的——这个赋值不会作用到数据库字段上,父类的oType还是会用默认值0,创建LinkObject时也不会自动设为1。下面给你几种正确的实现方式:
方案1:重写字段默认值(推荐)
在子类里重写oType字段的默认值,这样创建子类实例时会自动设置对应的类型值:
class PortfolioObject(models.Model): TYPE_GALLERY = 0 TYPE_LINK = 1 types = ( (TYPE_GALLERY, "gallery"), (TYPE_LINK, "link") ) name = models.CharField(max_length=100) oType = models.IntegerField(choices=types, default=TYPE_GALLERY) class Meta: # abstract=False是默认值,可省略 pass class GalleryObject(PortfolioObject): picture = models.FileField(blank=True) # 重写默认值,确保创建GalleryObject时自动设为0 oType = models.IntegerField(choices=PortfolioObject.types, default=PortfolioObject.TYPE_GALLERY) class LinkObject(PortfolioObject): link = models.URLField() # 设置LinkObject的oType默认值为1 oType = models.IntegerField(choices=PortfolioObject.types, default=PortfolioObject.TYPE_LINK)
这种方式下,父类和子类的oType对应同一个数据库字段,重写只是修改默认值,不会新增字段。
方案2:用信号自动设置类型
如果不想重复定义字段,可以用Django的pre_save信号,在保存子类实例时自动赋值:
from django.db.models.signals import pre_save from django.dispatch import receiver class PortfolioObject(models.Model): TYPE_GALLERY = 0 TYPE_LINK = 1 types = ( (TYPE_GALLERY, "gallery"), (TYPE_LINK, "link") ) name = models.CharField(max_length=100) oType = models.IntegerField(choices=types, default=TYPE_GALLERY) class GalleryObject(PortfolioObject): picture = models.FileField(blank=True) class LinkObject(PortfolioObject): link = models.URLField() # 给GalleryObject绑定保存前的信号 @receiver(pre_save, sender=GalleryObject) def set_gallery_type(sender, instance, **kwargs): instance.oType = PortfolioObject.TYPE_GALLERY # 给LinkObject绑定保存前的信号 @receiver(pre_save, sender=LinkObject) def set_link_type(sender, instance, **kwargs): instance.oType = PortfolioObject.TYPE_LINK
这种方式不用重复写字段定义,适合父类字段较多的场景。
方案3:使用抽象基类(无需父类实例时)
如果不需要直接创建PortfolioObject的实例,更推荐把它设为抽象基类,这样数据库不会生成父类的表,子类各自拥有独立的表:
class PortfolioObject(models.Model): TYPE_GALLERY = 0 TYPE_LINK = 1 types = ( (TYPE_GALLERY, "gallery"), (TYPE_LINK, "link") ) name = models.CharField(max_length=100) class Meta: abstract = True # 标记为抽象基类 class GalleryObject(PortfolioObject): oType = models.IntegerField(choices=PortfolioObject.types, default=PortfolioObject.TYPE_GALLERY) picture = models.FileField(blank=True) class LinkObject(PortfolioObject): oType = models.IntegerField(choices=PortfolioObject.types, default=PortfolioObject.TYPE_LINK) link = models.URLField()
这种方式更符合“子类是不同类型作品集对象”的语义,父类只是通用模板,不会有实际数据库记录。
实用小提示
- 用常量(比如
TYPE_GALLERY)代替魔法数字,代码更易读,后期修改也更省心。 - 单表继承时,Django会自动添加
portfolioobject_ptr_id字段关联父类记录,你不用手动处理。 - 查询父类时可以过滤特定子类实例:
# 获取所有GalleryObject实例 galleries = PortfolioObject.objects.instance_of(GalleryObject)
内容的提问来源于stack exchange,提问作者Christian Påbøl Jacobsen
相关产品推荐
相关产品推荐

