You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django REST Framework的API中展示TabularInline?

影院排期管理系统API开发及TabularInline展示方案

嘿,我来帮你搞定影院排期系统的API开发,还有你问的DRF里怎么实现类似TabularInline的展示——毕竟Django Admin的Inline在后台用着爽,但API里得换个思路来搞~

一、先梳理&补全你的模型关系

从你给的代码来看,核心模型的逻辑已经有了:

  • Movie是影片主表,存标题、简介、年份这些基础信息
  • Exhibition关联单部影片,记录它的上映起止日期
  • Schedule应该是具体的场次信息(你代码没写完,我补个常见的结构方便后续示例):
class Schedule(models.Model):
    CINE_DERBY = 'CD'
    CINE_CASAFO = 'CC'
    CINEMA_CHOICES = [
        (CINE_DERBY, 'Cine Derby'),
        (CINE_CASAFO, 'Cine Casafo'),
    ]
    exhibition = models.ForeignKey(Exhibition, verbose_name='Exibição', on_delete=models.CASCADE, related_name='schedules')
    cinema = models.CharField('Cinema', max_length=2, choices=CINEMA_CHOICES)
    show_time = models.DateTimeField('Horário de sessão')
    available_seats = models.IntegerField('Assentos disponíveis')

这里给exhibition外键加了related_name='schedules',后续查询和序列化会更顺手。

二、DRF中实现类似TabularInline的嵌套展示

Django Admin的TabularInline是在父模型页面展示关联子模型的表格,放到DRF里,我们用嵌套序列化器就能实现同样的效果——让父资源的API响应里直接包含关联的子资源列表。

1. 编写嵌套序列化器

我们从最底层的场次开始往上写:

第一步:场次(Schedule)序列化器

from rest_framework import serializers
from .models import Movie, Exhibition, Schedule

class ScheduleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Schedule
        fields = ['id', 'cinema', 'show_time', 'available_seats']
        # 把影院的缩写转换成友好名称返回给前端
        extra_kwargs = {'cinema': {'source': 'get_cinema_display'}}

第二步:上映排期(Exhibition)序列化器

这里把场次列表嵌套进去,就像Admin里把Schedule作为Exhibition的TabularInline一样:

class ExhibitionSerializer(serializers.ModelSerializer):
    # many=True表示是多个场次,read_only=True如果不需要通过Exhibition创建场次的话
    schedules = ScheduleSerializer(many=True, read_only=True)
    # 直接返回影片的标题(对应Movie的__str__方法,建议你给Movie加个__str__返回title)
    movie = serializers.StringRelatedField()

    class Meta:
        model = Exhibition
        fields = ['id', 'movie', 'start', 'finish', 'schedules']

第三步:影片(Movie)序列化器(可选)

如果想在影片的API里直接展示所有上映排期和对应场次,继续嵌套就行:

class MovieSerializer(serializers.ModelSerializer):
    exhibitions = ExhibitionSerializer(many=True, read_only=True)

    class Meta:
        model = Movie
        fields = ['id', 'title', 'synopsis', 'year', 'exhibitions']

2. 视图层快速搭建API

用DRF的视图集(ViewSet)来快速生成CRUD接口:

from rest_framework import viewsets
from .models import Movie, Exhibition, Schedule
from .serializers import MovieSerializer, ExhibitionSerializer, ScheduleSerializer

class MovieViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Movie.objects.all().prefetch_related('exhibition_set')
    serializer_class = MovieSerializer

class ExhibitionViewSet(viewsets.ModelViewSet):
    # 预加载场次数据,避免N+1查询问题
    queryset = Exhibition.objects.all().select_related('movie').prefetch_related('schedules')
    serializer_class = ExhibitionSerializer

class ScheduleViewSet(viewsets.ModelViewSet):
    queryset = Schedule.objects.all().select_related('exhibition__movie')
    serializer_class = ScheduleSerializer

这里的select_related和prefetch_related一定要加,不然数据量大的时候API会慢到离谱。

3. 实现可编辑的嵌套序列化器(可选)

如果需要像Admin Inline那样,创建/更新上映排期的时候同时处理场次,重写序列化器的create和update方法就行:

class ExhibitionSerializer(serializers.ModelSerializer):
    schedules = ScheduleSerializer(many=True)

    class Meta:
        model = Exhibition
        fields = ['id', 'movie', 'start', 'finish', 'schedules']

    def create(self, validated_data):
        # 先提取场次数据,再创建排期
        schedules_data = validated_data.pop('schedules')
        exhibition = Exhibition.objects.create(**validated_data)
        for schedule_data in schedules_data:
            Schedule.objects.create(exhibition=exhibition, **schedule_data)
        return exhibition

    def update(self, instance, validated_data):
        schedules_data = validated_data.pop('schedules')
        # 更新排期本身的字段
        instance.movie = validated_data.get('movie', instance.movie)
        instance.start = validated_data.get('start', instance.start)
        instance.finish = validated_data.get('finish', instance.finish)
        instance.save()

        # 更新现有场次、创建新场次、删除未提交的旧场次
        existing_schedules = {s.id: s for s in instance.schedules.all()}
        for schedule_data in schedules_data:
            schedule_id = schedule_data.get('id')
            if schedule_id and schedule_id in existing_schedules:
                # 更新已有场次
                schedule = existing_schedules[schedule_id]
                schedule.cinema = schedule_data.get('cinema', schedule.cinema)
                schedule.show_time = schedule_data.get('show_time', schedule.show_time)
                schedule.available_seats = schedule_data.get('available_seats', schedule.available_seats)
                schedule.save()
            else:
                # 创建新场次
                Schedule.objects.create(exhibition=instance, **schedule_data)
        # 删除不在提交数据里的旧场次(按需开启)
        for schedule_id in existing_schedules:
            if schedule_id not in [s.get('id') for s in schedules_data if 'id' in s]:
                existing_schedules[schedule_id].delete()
        return instance

三、额外优化小 tips

  • 权限控制:如果API需要登录才能访问,加个permission_classes = [IsAuthenticated]到视图集里就行,也可以自定义权限逻辑。
  • 过滤排序:装个django-filter库,就能实现按影片标题、影院、上映时间过滤的功能,前端体验会好很多。
  • 分页:给视图集加个pagination_class = PageNumberPagination,避免一次性返回太多数据。

内容的提问来源于stack exchange,提问作者LSM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:36:39