如何在Django自定义模型字段中实现选课规则自定义验证?
Django学生课程选择验证解决方案
先修正原模型的明显错误
language字段的choices错误引用了Math.choices,应改为Language.choices- 原模型仅支持单选Science课程,无法满足“1-2门”的需求
- 缺少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
相关产品推荐
相关产品推荐

