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

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')

额外注意事项

  1. 运行python manage.py makemigrations和python manage.py migrate来应用模型修改
  2. 如果之前已经创建了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)
  1. 确保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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 21:57:00