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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 23:16:01