Django多对多字段排序问题:日程表科目顺序无法自定义
解决Django日程表系统科目顺序自定义问题
问题描述
开发基于Django的学校日程表系统,现有SubjectList(科目列表)和Day(日期)两个模型,Day通过多对多字段关联SubjectList。需要实现不同日期的科目顺序可自定义,但目前无论添加科目时的顺序如何,所有日期的科目排列顺序完全一致:
- 期望效果:
- Monday - English, maths, science, Social Science
- Tuesday - Maths, Social Science, English, Math's
- 实际效果:
- Monday - English, maths, science, Social Science
- Tuesday - English, maths, science, Social Science
现有代码
Models.py
class SubjectList(models.Model): subject_name = models.CharField(max_length=25) def __str__(self): return self.subject_name
class Day(models.Model): day_name = models.CharField(max_length=15) subject_name = models.ManyToManyField(SubjectList) class_number = models.ForeignKey(AddClass, on_delete=models.CASCADE, null=True, blank=True) start_time = models.TimeField(null=True, blank=True) end_time = models.TimeField(null=True, blank=True) def __str__(self): return self.class_number.class_number
Views.py
class TimeTableView(APIView): def get(self, request, id): class_number = AddClass.objects.get(id=id) day = Day.objects.filter(class_number=class_number.id) print(day) serializer = DaySerializer(day, many=True) return Response(serializer.data)
问题原因
Django默认的多对多关系不会保存关联对象的添加顺序,查询时会默认按关联模型的主键排序,因此所有Day实例返回的科目顺序完全一致。
解决方案
要实现自定义顺序,需要让多对多关系保存添加顺序,以下是两种常用实现方式:
方式1:自定义中间表手动管理顺序
创建中间表存储科目与日期的关联,并新增order字段控制排序:
- 修改模型:
class SubjectList(models.Model): subject_name = models.CharField(max_length=25) def __str__(self): return self.subject_name # 自定义中间表,保存科目顺序 class DaySubject(models.Model): day = models.ForeignKey(Day, on_delete=models.CASCADE) subject = models.ForeignKey(SubjectList, on_delete=models.CASCADE) order = models.IntegerField(default=0) class Meta: ordering = ['order'] # 按order字段排序 class Day(models.Model): day_name = models.CharField(max_length=15) # 指定中间表及关联字段 subject_name = models.ManyToManyField(SubjectList, through=DaySubject, through_fields=('day', 'subject')) class_number = models.ForeignKey(AddClass, on_delete=models.CASCADE, null=True, blank=True) start_time = models.TimeField(null=True, blank=True) end_time = models.TimeField(null=True, blank=True) def __str__(self): return self.class_number.class_number
- 更新数据库:
执行以下命令迁移数据库:
python manage.py makemigrations python manage.py migrate
- 添加科目时指定顺序:
通过中间表创建关联并设置order值:
# 示例:给Monday按指定顺序添加科目 monday = Day.objects.get(day_name="Monday") english = SubjectList.objects.get(subject_name="English") maths = SubjectList.objects.get(subject_name="maths") DaySubject.objects.create(day=monday, subject=english, order=1) DaySubject.objects.create(day=monday, subject=maths, order=2)
- 序列化保持顺序:
Django REST Framework的ModelSerializer会自动遵循中间表的Meta.ordering配置,序列化时会按自定义顺序返回科目列表。
方式2:自动按添加顺序保存
如果无需手动设置顺序,仅需保留添加时的顺序,可以让中间表自动生成order值:
- 修改中间表模型:
class DaySubject(models.Model): day = models.ForeignKey(Day, on_delete=models.CASCADE) subject = models.ForeignKey(SubjectList, on_delete=models.CASCADE) order = models.IntegerField() class Meta: ordering = ['order'] unique_together = ['day', 'subject'] def save(self, *args, **kwargs): # 自动生成order值:当前日期下最大order+1 if not self.order: max_order = DaySubject.objects.filter(day=self.day).aggregate(Max('order'))['order__max'] or 0 self.order = max_order + 1 super().save(*args, **kwargs)
- 添加科目简化操作:
直接使用add方法添加科目,系统会自动按添加顺序分配order:
monday.subject_name.add(english, maths, science) # 顺序会被自动保存
内容的提问来源于stack exchange,提问作者Sins97
相关产品推荐
相关产品推荐

