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

如何在Django自定义模型字段中实现选课规则自定义验证?

Django学生课程选择验证解决方案

先修正原模型的明显错误

  1. language字段的choices错误引用了Math.choices,应改为Language.choices
  2. 原模型仅支持单选Science课程,无法满足“1-2门”的需求
  3. 缺少Art、Society课程的存储字段,无法实现跨类别选课验证

修正后的模型代码(支持多选与验证)

from django.db import models
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError

class Student(models.Model):
    student_number = models.PositiveIntegerField()
    first_name = models.CharField(max_length=50) 
    last_name = models.CharField(max_length=50) 
    email = models.EmailField(max_length=100)

    class English(models.TextChoices):
        LANGLIT = 'LL', _('Language and literature')
        LIT = 'L', _('Literature')

    english = models.CharField(max_length=2, choices=English.choices, default=English.LANGLIT)

    class Math(models.TextChoices):
        AA = 'AA', _('Analysis & Approaches')
        AI = 'AI', _('Analysis & Interpretation')

    math = models.CharField(max_length=2, choices=Math.choices, default=Math.AA)

    class Language(models.TextChoices):
        FRENCH = 'F', _('French')
        SPANISH = 'S', _('Spanish')
        ARABIC = 'A', _('Arabic')
        MANDARIN = 'M', _('Mandarin')

    # 修正choices引用错误
    language = models.CharField(max_length=1, choices=Language.choices, default=Language.FRENCH)

    class Science(models.TextChoices):
        BIOLOGY = 'BIO', _('Biology')
        CHEMISTRY = 'CHEM', _('Chemistry')
        PHYSICS = 'PHY', _('Physics')
        COMPUTERSCIENCE = 'CS', _('Computer Science')
        DESIGNTECHNOLOGY = 'DT', _('Design Technology')
        ESS = 'ESS', _('Environmental Systems and Societies')

    # 使用ArrayField支持多选(需PostgreSQL),size限制最多选2门
    science = models.ArrayField(
        models.CharField(max_length=4, choices=Science.choices),
        size=2,
        default=list,
        help_text="选择1-2门科学课程"
    )

    class Society(models.TextChoices):
        MANAGEMENT = 'BM', _('Business Management')
        ECONOMICS = 'ECON', _('Economics')
        GEOGRAPHY = 'GEO', _('Geography')
        GLOBALPOLITICS = 'GP', _('Global Politics')
        HISTORY = 'HIS', _('History')
        PSYCHOLOGY = 'PSYCH', _('Psychology')

    society = models.ArrayField(
        models.CharField(max_length=5, choices=Society.choices),
        size=3,
        default=list,
        help_text="可选社会科学课程"
    )

    class Art(models.TextChoices):
        VISUALARTS = 'VA', _('Visual Arts')
        MUSIC = 'MUS', _('Music')
        FILM = 'FILM', _('Film')

    art = models.ArrayField(
        models.CharField(max_length=5, choices=Art.choices),
        size=3,
        default=list,
        help_text="可选艺术课程"
    )

    def clean(self):
        super().clean()
        errors = {}

        # 场景1:English和Math至少选1门(当前字段默认有值,若要允许不选需修改字段为null=True, blank=True,再启用下方验证)
        # if not self.english and not self.math:
        #     errors['english'] = "必须选择英语或数学课程中的至少一门"
        #     errors['math'] = "必须选择英语或数学课程中的至少一门"

        # 场景2:验证Science课程数量与重复
        science_courses = self.science
        if len(science_courses) < 1:
            errors['science'] = "必须选择至少1门科学课程"
        elif len(science_courses) > 2:
            errors['science'] = "最多只能选择2门科学课程"
        elif len(set(science_courses)) != len(science_courses):
            errors['science'] = "科学课程不能重复选择"

        # 场景3:验证跨类别选课总数与重复
        all_elective_courses = []
        all_elective_courses.extend(self.science)
        all_elective_courses.extend(self.art)
        all_elective_courses.extend(self.society)
        # 过滤空值(若允许某类课程不选)
        all_elective_courses = [course for course in all_elective_courses if course]
        
        if len(all_elective_courses) != 3:
            errors['__all__'] = "必须从科学、艺术、社会科学中总共选择3门课程"
        elif len(set(all_elective_courses)) != len(all_elective_courses):
            errors['__all__'] = "所有选修课程不能重复"

        if errors:
            raise ValidationError(errors)

    def __str__(self):
        return f'{self.first_name} {self.last_name}'

非PostgreSQL数据库替代方案

如果使用MySQL等不支持ArrayField的数据库,可改用CharField存储逗号分隔的课程代码,修改字段与验证逻辑如下:

# 替换Science字段定义
science = models.CharField(max_length=20, blank=True, help_text="选择1-2门科学课程,用逗号分隔")

# 在clean方法中处理Science验证
science_courses = [s.strip() for s in self.science.split(',') if s.strip()]
# 后续数量与重复验证逻辑和之前一致

前端表单优化(可选)

为了让用户更直观地选择课程,可在ModelForm中使用多选复选框控件:

from django import forms
from .models import Student

class StudentForm(forms.ModelForm):
    class Meta:
        model = Student
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为多选字段替换为复选框控件
        self.fields['science'].widget = forms.CheckboxSelectMultiple(choices=Student.Science.choices)
        self.fields['art'].widget = forms.CheckboxSelectMultiple(choices=Student.Art.choices)
        self.fields['society'].widget = forms.CheckboxSelectMultiple(choices=Student.Society.choices)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:05:52