Django 多表继承(Multi-table Inheritance)子类属性的访问

最近在开发一套活动预约系统,其中电影类活动牵涉到选座购票,而其他类型的活动则一般只需展示或提供记名预约即可。根据具体场景,具体关系设计如下:

多表继承结构
多表继承结构

对于 B 基于 A 的多表继承,Django ORM 的处理方式是:生成表 a 与表 b,并在表 b 中自动添加 OneToOneField 列 a.ptr。在上图中,对于 event.Event 和 movieticket.MovieShowtime 两个多表继承关系的模型,在 shell 中是这样访问的:

>>> e = Event.objects.get(pk=1)
>>> ms = e.movieshowtime

在父类中,我们定义了 event_type 的 choices 可选值,

class Event(models.Model):
    EVENT_TYPE_MOVIE = 'M'
    EVENT_TYPE_WORKSHOP = 'W'
    EVENT_TYPE_TALK = 'T'
    EVENT_TYPES = (
        (EVENT_TYPE_MOVIE, '电影放映 / Movie Screening'),
        (EVENT_TYPE_WORKSHOP, '工作坊 / Workshop'),
        (EVENT_TYPE_TALK, '讲座 / Talk'),
    )

并在子类中定义表示活动种类的 event_type 这一 CharField。

class MovieShowtime(Event):
    event_type = models.CharField(
        max_length=2,
        choices=Event.EVENT_TYPES,
        default=Event.EVENT_TYPE_MOVIE,
        editable=False)

若不这样做,则我们可能需要覆盖重写改变初始化模型类实例的方法来默认记录类型信息。(Otherwise, we would need to override create method of model instances to have type info recorded by default.)更优雅的方式请见文末。

我们约定,所有子类都有 event_type 这一属性,为了让后续操作可访问该域,我们可以在父类增添一个方法。

class Event(models.Model):
    pass

    def event_type(self):
        for subcls in ('movieshowtime', 'workshop', 'talk'):
            try:
                subcls_instance = getattr(self, subcls)
                return {
                    'abbr': subcls_instance.event_type,
                    'full': subcls_instance.get_event_type_display(),
                }
            except:
                pass
        return None

当然,我们也可以使用更优雅的方式(https://www.djangosnippets.org/snippets/1034/)来实现这一效果。