Django开发:如何跳过未填充成绩的学期模型避免DoesNotExist错误
问题解决与优化方案
一、临时解决:避免DoesNotExist错误并显示“No Data”
当前视图中使用get()方法会在无对应成绩数据时抛出异常,可通过以下两种方式处理:
方法1:用try-except捕获异常
修改视图函数,对每个成绩查询捕获DoesNotExist异常,无数据时将变量设为None,后续在模板中判断显示内容:
from django.shortcuts import render from .models import Students, Achievement, Grades1, Grades2 from django.core.exceptions import DoesNotExist def users(response, int): s = Students.objects.get(nim=int) # 处理第一学期成绩 try: sg1 = s.grades1_set.get(nim=int) except DoesNotExist: sg1 = None # 处理第二学期成绩 try: sg2 = s.grades2_set.get(nim=int) except DoesNotExist: sg2 = None sa = s.achievement_set.all() # 同理处理Accounts,避免无数据报错 try: sac = s.accounts_set.get(nim=int) except DoesNotExist: sac = None return render(response, 'users/user.html', { 's': s, 'sg1': sg1, 'sg2': sg2, 'sa': sa, 'sac': sac })
模板user.html中添加判断逻辑:
<!-- 第一学期成绩展示 --> <h3>第一学期成绩</h3> {% if sg1 %} <p>生物:{{ sg1.biology }}</p> <p>数学:{{ sg1.math }}</p> <p>历史:{{ sg1.history }}</p> {% else %} <p>No Data</p> {% endif %} <!-- 第二学期成绩展示 --> <h3>第二学期成绩</h3> {% if sg2 %} <p>生物:{{ sg2.biology }}</p> <p>数学:{{ sg2.math }}</p> <p>历史:{{ sg2.history }}</p> {% else %} <p>No Data</p> {% endif %}
方法2:用filter().first()替代get()
filter()返回查询集,first()会返回第一个匹配对象或None,不会抛出异常,代码更简洁:
def users(response, int): s = Students.objects.get(nim=int) sg1 = s.grades1_set.filter(nim=int).first() sg2 = s.grades2_set.filter(nim=int).first() sa = s.achievement_set.all() sac = s.accounts_set.filter(nim=int).first() return render(response, 'users/user.html', { 's': s, 'sg1': sg1, 'sg2': sg2, 'sa': sa, 'sac': sac })
模板判断逻辑和方法1一致。
二、更优实现方案:重构成绩模型
当前每个学期单独创建模型的方式扩展性极差,后续新增学期需重复创建模型、修改视图和模板,维护成本高。建议合并为单一成绩模型,新增学期字段区分不同学期:
重构后的模型代码
from django.db import models class Students(models.Model): class_ = models.CharField(max_length=6, default='no data') # 避免和Python关键字冲突,重命名clas为class_ nim = models.IntegerField(primary_key=True) name = models.CharField(max_length=50) year = models.IntegerField() def __str__(self): return f"{self.nim} in class {self.class_}" # 合并所有学期成绩为一个模型 class Grades(models.Model): TERM_CHOICES = [ (1, '第一学期'), (2, '第二学期'), (3, '第三学期'), # 后续新增学期只需在此添加选项 ] nim = models.ForeignKey(Students, on_delete=models.CASCADE) term = models.IntegerField(choices=TERM_CHOICES) # 新增学期字段 biology = models.IntegerField(default=0) math = models.IntegerField(default=0) history = models.IntegerField(default=0) # 确保每个学生每个学期只有一条成绩记录 class Meta: unique_together = ('nim', 'term') def __str__(self): return f"{self.nim.name} 第{self.term}学期成绩:生物={self.biology} 数学={self.math} 历史={self.history}" # 其他模型(Achievement、Accounts)保持不变,建议Accounts的passwords改为password(单数更合理)
重构后的视图代码
查询学生所有学期成绩,无需单独处理每个学期:
def users(response, int): s = Students.objects.get(nim=int) # 获取该学生所有学期的成绩,按学期排序 grades = s.grades_set.all().order_by('term') sa = s.achievement_set.all() sac = s.accounts_set.filter(nim=int).first() # 整理为字典,方便模板按学期快速查找 grade_dict = {g.term: g for g in grades} return render(response, 'users/user.html', { 's': s, 'grades': grades, 'grade_dict': grade_dict, 'sa': sa, 'sac': sac })
重构后的模板代码
可灵活显示所有有数据的学期,或指定学期:
<!-- 显示所有有成绩的学期 --> <h3>所有学期成绩</h3> {% for grade in grades %} <h4>第{{ grade.term }}学期</h4> <p>生物:{{ grade.biology }}</p> <p>数学:{{ grade.math }}</p> <p>历史:{{ grade.history }}</p> {% empty %} <p>No Data</p> {% endfor %} <!-- 指定显示第一学期成绩 --> <h3>第一学期成绩</h3> {% if grade_dict.1 %} <p>生物:{{ grade_dict.1.biology }}</p> <p>数学:{{ grade_dict.1.math }}</p> <p>历史:{{ grade_dict.1.history }}</p> {% else %} <p>No Data</p> {% endif %}
三、额外建议
- Accounts模型优化:不要明文存储密码,使用Django自带的
AbstractUser或AbstractBaseUser处理用户认证,自动加密密码,安全性更高。 - 字段命名规范:避免使用Python关键字(如
class),字段名尽量使用单数形式(如password替代passwords)。
内容的提问来源于stack exchange,提问作者isca_
相关产品推荐
相关产品推荐

