Django开发考勤系统for循环写库出现重复记录如何解决
问题背景
开发人脸考勤功能时的基础逻辑:网络摄像头捕获到学生人脸图像后,尝试将对应学生记录写入考勤数据库。
- name:已存储的人脸图像文件名,取值和Student实体中存储的
student_id字段值一致 - Names:所有已存储人脸图像文件名组成的列表
- 原有触发规则:检测到的人脸对应的
name存在于Names列表中时,就将该学生信息写入考勤数据库
当前代码可正常运行,但会持续重复插入考勤记录,需要调整逻辑限制同一条记录仅写入一次,避免数据重复存储。
原有问题代码:
def markattendance(name): for n in Names: if name in Names: # print(name, "Exist") # 获取检测到姓名对应的学生信息 attend = Student.objects.filter(student_id=name).values('student_id', 'student_fname', 'student_lname','fk_course_id_id') #print(attend) # filter返回的是查询集,不是单个对象,不能直接对查询集调用save(),需要遍历内部对象逐个保存 for object in attend: #print(object) if object.get('student_id') not in attend: # 调用save()时Django才会执行INSERT SQL操作数据库 reg = Attendance(student_id=object.get('student_id'), student_fname=object.get('student_fname'), student_lname=object.get('student_lname'), course_id=object.get('fk_course_id_id')) # print(reg) reg.save() else: pass
问题原因
原有代码有3个核心逻辑错误,才会导致重复插入:
- 外层
for n in Names是完全无效的冗余循环,只要name在Names列表中,每调用一次函数、每遍历一次列表元素都会触发后续的数据库查询、写入逻辑 - 重复校验的判断对象完全错误:原有判断
object.get('student_id') not in attend里的attend是从Student表查出来的学生信息查询集,本来就是按student_id=name查询的学生,student_id必然存在于attend中,这个判断逻辑完全起不到“是否已经打过卡”的校验作用 - 没有数据库层面的唯一约束兜底,只要代码逻辑有漏洞,就会持续写入重复数据
修复方案
1. 调整业务判断逻辑
首先删掉无效的外层循环,写入考勤记录前,先去Attendance表查询当前学生对应考勤维度下是否已经存在记录,不存在才执行写入。
注:考勤唯一维度可根据自己的业务调整,示例按「同一名学生、同一门课程、同一天只允许打一次卡」的通用规则实现
修正后的函数代码:
import datetime def markattendance(name): # 先判断识别到的name是否在合法学生名单里,去掉冗余的for循环 if name not in Names: return # 获取当前日期,作为同一次考勤的判断维度,如果是固定考勤场次可以换成对应场次id today = datetime.date.today() # 获取对应学生信息,student_id是唯一标识,用first()代替遍历values查询集,逻辑更简洁 student = Student.objects.filter(student_id=name).first() if not student: return # 核心校验:查Attendance表是否已经有该学生今日对应课程的打卡记录 has_attended = Attendance.objects.filter( student_id=student.student_id, course_id=student.fk_course_id_id, # 如果是按天考勤就加日期判断,固定场次就换成attendance_session_id=xxx attendance_time__date=today ).exists() # 没有打卡记录才执行写入 if not has_attended: Attendance.objects.create( student_id=student.student_id, student_fname=student.student_fname, student_lname=student.student_lname, course_id=student.fk_course_id_id )
2. 加数据库层面唯一约束兜底
光改代码逻辑还不够,遇到摄像头短时间重复识别、并发请求的场景还是可能出现重复插入,需要在Attendance模型里加联合唯一约束,从数据库层面拦截重复数据:
from django.db import models class Attendance(models.Model): # 原有字段定义可保留,补充自动记录打卡时间的字段 student_id = models.CharField(max_length=50) student_fname = models.CharField(max_length=50) student_lname = models.CharField(max_length=50) course_id = models.IntegerField() attendance_time = models.DateTimeField(auto_now_add=True) class Meta: # 加联合唯一约束:同一个学生、同一门课、同一天只能有一条打卡记录 constraints = [ models.UniqueConstraint( fields=['student_id', 'course_id'], name='unique_student_course_daily_attend', # 针对datetime类型的时间字段,按日期维度去重,Django4.0+支持该写法 expression=models.expressions.TruncDate('attendance_time') ) ]
加完约束之后记得依次执行python manage.py makemigrations和python manage.py migrate让约束生效,就算后续代码逻辑出问题,重复的记录也会被数据库直接拦截,不会产生脏数据。
内容的提问来源于stack exchange,提问作者Zoe
相关产品推荐
相关产品推荐

