Django中RelatedObjectDoesNotExist错误:用户未关联Faculty对象求助
解决Django考勤系统"users has no faculty"错误
错误原因
你的代码中Faculty模型与Django内置User模型是一对一关联,但系统没有为新创建的User自动生成对应的Faculty记录。当视图中直接调用request.user.faculty时(比如takeAttendence和facultyProfile函数),如果当前登录用户没有关联的Faculty实例,就会触发RelatedObjectDoesNotExist异常,提示"users has no faculty"。
另外还有两处小问题:
facultyProfile视图中faculty = request.user.faculty,多了一个逗号,导致faculty变成元组,传递给表单时会报错Attendence模型中用字符串存储Faculty_Name,不如直接关联Faculty外键更规范,也能避免因字符串不一致导致的问题
修正方案
1. 添加自动创建Faculty的信号
在models.py中添加Django信号,当新User创建时自动生成对应的Faculty实例,确保每个用户都有关联的Faculty记录。
2. 修复视图中的错误
- 移除
facultyProfile中的多余逗号 - 在访问
request.user.faculty前增加异常捕获,或者确保信号已经自动创建了记录 - 优化
Attendence模型,改用外键关联Faculty(可选但推荐)
3. 完善用户引导
登录后如果用户没有完善Faculty信息,引导用户填写。
完整修正代码
修改后的models.py
from django.db import models from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver def user_directory_path(instance, filename): name, ext = filename.split(".") name = instance.firstname + instance.lastname filename = name +'.'+ ext return 'Faculty_Images/{}'.format(filename) class Faculty(models.Model): user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE) firstname = models.CharField(max_length=200, null=True, blank=True) lastname = models.CharField(max_length=200, null=True, blank=True) phone = models.CharField(max_length=200, null=True) email = models.CharField(max_length=200, null=True) profile_pic = models.ImageField(upload_to=user_directory_path ,null=True, blank=True) def __str__(self): return f"{self.firstname} {self.lastname}" if self.firstname and self.lastname else self.user.username # 信号:创建User时自动创建对应的Faculty @receiver(post_save, sender=User) def create_user_faculty(sender, instance, created, **kwargs): if created: Faculty.objects.create(user=instance) @receiver(post_save, sender=User) def save_user_faculty(sender, instance, **kwargs): instance.faculty.save() def student_directory_path(instance, filename): name, ext = filename.split(".") name = instance.registration_id filename = name +'.'+ ext return 'Student_Images/{}/{}/{}/{}'.format(instance.branch,instance.year,instance.section,filename) class Student(models.Model): BRANCH = ( ('CSE','CSE'), ('IT','IT'), ('ECE','ECE'), ('CHEM','CHEM'), ('MECH','MECH'), ('EEE','EEE'), ) YEAR = ( ('1','1'), ('2','2'), ('3','3'), ('4','4'), ) SECTION = ( ('A','A'), ('B','B'), ('C','C'), ) firstname = models.CharField(max_length=200, null=True, blank=True) lastname = models.CharField(max_length=200, null=True, blank=True) registration_id = models.CharField(max_length=200, null=True, unique=True) branch = models.CharField(max_length=100, null=True, choices=BRANCH) year = models.CharField(max_length=100, null=True, choices=YEAR) section = models.CharField(max_length=100, null=True, choices=SECTION) profile_pic = models.ImageField(upload_to=student_directory_path ,null=True, blank=True) def __str__(self): return str(self.registration_id) class Attendence(models.Model): # 改用外键关联Faculty,更规范 faculty = models.ForeignKey(Faculty, null=True, on_delete=models.SET_NULL) student = models.ForeignKey(Student, null=True, on_delete=models.SET_NULL) date = models.DateField(auto_now_add=True, null=True) time = models.TimeField(auto_now_add=True, null=True) branch = models.CharField(max_length=200, null=True) year = models.CharField(max_length=200, null=True) section = models.CharField(max_length=200, null=True) period = models.CharField(max_length=200, null=True) status = models.CharField(max_length=200, null=True, default='Absent') def __str__(self): return f"{self.student.registration_id}_{self.date}_{self.period}" if self.student else f"Unknown_{self.date}_{self.period}"
修改后的views.py
from django.shortcuts import render, redirect from django.http import HttpResponse, StreamingHttpResponse from django.contrib import messages from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from django.core.exceptions import RelatedObjectDoesNotExist from .forms import * from .models import Student, Attendence, Faculty from .filters import AttendenceFilter from .recognizer import Recognizer from datetime import date @login_required(login_url='login') def home(request): # 检查用户是否完善了Faculty信息 try: faculty = request.user.faculty if not faculty.firstname or not faculty.lastname: messages.warning(request, "请先完善您的教师信息") return redirect('facultyProfile') except RelatedObjectDoesNotExist: # 极端情况,信号没触发,手动创建 Faculty.objects.create(user=request.user) messages.warning(request, "请完善您的教师信息") return redirect('facultyProfile') studentForm = CreateStudentForm() if request.method == 'POST': studentForm = CreateStudentForm(data=request.POST, files=request.FILES) stat = False try: Student.objects.get(registration_id=request.POST['registration_id']) stat = True except Student.DoesNotExist: stat = False if studentForm.is_valid() and not stat: studentForm.save() name = f"{studentForm.cleaned_data.get('firstname')} {studentForm.cleaned_data.get('lastname')}" messages.success(request, f'学生 {name} 添加成功') return redirect('home') else: messages.error(request, f'学号 {request.POST["registration_id"]} 已存在') return redirect('home') context = {'studentForm': studentForm} return render(request, 'attendence_sys/home.html', context) def loginPage(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user = authenticate(request, username=username, password=password) if user is not None: login(request, user) return redirect('home') else: messages.info(request, '用户名或密码错误') context = {} return render(request, 'attendence_sys/login.html', context) @login_required(login_url='login') def logoutUser(request): logout(request) return redirect('login') @login_required(login_url='login') def updateStudentRedirect(request): context = {} if request.method == 'POST': try: reg_id = request.POST['reg_id'] branch = request.POST['branch'] student = Student.objects.get(registration_id=reg_id, branch=branch) updateStudentForm = CreateStudentForm(instance=student) context = {'form': updateStudentForm, 'prev_reg_id': reg_id, 'student': student} except Student.DoesNotExist: messages.error(request, '未找到该学生') return redirect('home') return render(request, 'attendence_sys/student_update.html', context) @login_required(login_url='login') def updateStudent(request): if request.method == 'POST': try: student = Student.objects.get(registration_id=request.POST['prev_reg_id']) updateStudentForm = CreateStudentForm(data=request.POST, files=request.FILES, instance=student) if updateStudentForm.is_valid(): updateStudentForm.save() messages.success(request, '更新成功') return redirect('home') except Student.DoesNotExist: messages.error(request, '更新失败') return redirect('home') return render(request, 'attendence_sys/student_update.html', context) @login_required(login_url='login') def takeAttendence(request): if request.method == 'POST': try: faculty = request.user.faculty if not faculty.firstname or not faculty.lastname: messages.error(request, "请先完善教师信息") return redirect('facultyProfile') except RelatedObjectDoesNotExist: messages.error(request, "教师信息不存在,请联系管理员") return redirect('home') details = { 'branch': request.POST['branch'], 'year': request.POST['year'], 'section': request.POST['section'], 'period': request.POST['period'], 'faculty': faculty, } # 检查是否已记录考勤 if Attendence.objects.filter( date=date.today(), branch=details['branch'], year=details['year'], section=details['section'], period=details['period'], faculty=faculty ).exists(): messages.error(request, "该时段考勤已记录") return redirect('home') else: students = Student.objects.filter( branch=details['branch'], year=details['year'], section=details['section'] ) names = Recognizer(details) for student in students: status = 'Present' if str(student.registration_id) in names else 'Absent' Attendence.objects.create( faculty=faculty, student=student, period=details['period'], branch=details['branch'], year=details['year'], section=details['section'], status=status ) attendences = Attendence.objects.filter( date=date.today(), branch=details['branch'], year=details['year'], section=details['section'], period=details['period'], faculty=faculty ) context = {"attendences": attendences, "ta": True} messages.success(request, "考勤记录成功") return render(request, 'attendence_sys/attendence.html', context) context = {} return render(request, 'attendence_sys/home.html', context) def searchAttendence(request): attendences = Attendence.objects.all() myFilter = AttendenceFilter(request.GET, queryset=attendences) attendences = myFilter.qs context = {'myFilter': myFilter, 'attendences': attendences, 'ta': False} return render(request, 'attendence_sys/attendence.html', context) @login_required(login_url='login') def facultyProfile(request): try: faculty = request.user.faculty except RelatedObjectDoesNotExist: faculty = Faculty.objects.create(user=request.user) if request.method == 'POST': form = FacultyForm(data=request.POST, files=request.FILES, instance=faculty) if form.is_valid(): form.save() messages.success(request, "信息保存成功") return redirect('home') else: form = FacultyForm(instance=faculty) context = {'form': form} return render(request, 'attendence_sys/facultyForm.html', context) # 以下视频相关代码保持不变 # class VideoCamera(object): # def __init__(self): # self.video = cv2.VideoCapture(0) # def __del__(self): # self.video.release() # def get_frame(self): # ret,image = self.video.read() # ret,jpeg = cv2.imencode('.jpg',image) # return jpeg.tobytes() # def gen(camera): # while True: # frame = camera.get_frame() # yield(b'--frame\r\n' # b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n') # @gzip.gzip_page # def videoFeed(request): # try: # return StreamingHttpResponse(gen(VideoCamera()),content_type="multipart/x-mixed-replace;boundary=frame") # except: # print("aborted") # def getVideo(request): # return render(request, 'attendence_sys/videoFeed.html')
额外注意事项
- 运行
python manage.py makemigrations和python manage.py migrate来应用模型修改 - 如果之前已经创建了User但没有对应的Faculty,可以通过Django后台手动创建,或者运行以下脚本批量生成:
# 在Django shell中执行 from django.contrib.auth.models import User from .models import Faculty for user in User.objects.all(): if not hasattr(user, 'faculty'): Faculty.objects.create(user=user)
- 确保
forms.py中存在FacultyForm和CreateStudentForm,如果没有,需要创建对应的ModelForm:
# forms.py示例 from django import forms from .models import Student, Faculty class CreateStudentForm(forms.ModelForm): class Meta: model = Student fields = ['firstname', 'lastname', 'registration_id', 'branch', 'year', 'section', 'profile_pic'] class FacultyForm(forms.ModelForm): class Meta: model = Faculty fields = ['firstname', 'lastname', 'phone', 'email', 'profile_pic']
内容的提问来源于stack exchange,提问作者SATHYA PRAKASH 20PZSB7041
相关产品推荐
相关产品推荐

