如何通过主键/外键关联三个Django模型?报错解决与需求实现
报错原因分析
- 错误使用
ManyToOneRel:models.ManyToOneRel是Django内部用于维护外键关系的底层类,并非定义模型字段的公开API;one_to_many是该类的缓存属性,不是可调用方法,直接调用会触发TypeError。 company_id关联对象错误:ReportDetails的company_id字段错误关联了settings.AUTH_USER_MODEL,不符合“以Company为核心关联”的需求,应关联Company模型。- Company与User的关系逻辑错误:原
Company模型的user外键设计导致一个公司仅能关联一个用户,无法满足“公司拥有多个员工”的场景。
符合需求的模型关联方案
核心关系梳理:
- 一对多:一个公司对应多个员工
- 一对多:一个公司对应多个报告,每个报告由一位员工创建
- 多对多(带中间模型):记录员工查看报告的行为,用于统计查看列表
修改后的完整模型代码:
from django.db import models from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager import uuid from django.conf import settings class UserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError('Users must have an email address') email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, password): user = self.create_user(email, password) user.is_staff = True user.is_superuser = True user.save(using=self._db) return user class User(AbstractBaseUser, PermissionsMixin): """系统用户模型""" email = models.EmailField(max_length=255, unique=True) fname = models.CharField(max_length=255) lname = models.CharField(max_length=255) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) phone_number = models.PositiveIntegerField(default=0) license = models.CharField(default="0", max_length=50) signature = models.ImageField(null=True, upload_to="signatures/") # 用户所属公司:一个用户属于一个公司,一个公司对应多个员工 company = models.ForeignKey( 'Company', on_delete=models.CASCADE, related_name='employees', null=True, blank=True ) objects = UserManager() USERNAME_FIELD = 'email' def __str__(self): return self.email class Company(models.Model): """公司模型""" company_id = models.UUIDField( primary_key=True, default=uuid.uuid4, editable=False, unique=True ) owner = models.CharField(max_length=30) owner_email = models.CharField(max_length=75) company_name = models.CharField(max_length=75) company_addr = models.CharField(max_length=255) phone_number = models.PositiveIntegerField(default=0) logo = models.ImageField(null=True, upload_to="logos/") def __str__(self): return self.company_name class ReportDetails(models.Model): """报告详情模型""" report_uuid = models.UUIDField( primary_key=True, default=uuid.uuid4, editable=False, unique=True ) # 报告所属公司:一个公司对应多个报告 company = models.ForeignKey( Company, on_delete=models.CASCADE, related_name='reports' ) # 报告创建者:一个用户创建多个报告 created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='created_reports' ) title = models.CharField(max_length=100) r_id = models.CharField(max_length=100) def __str__(self): return self.title # 记录用户查看报告的行为,用于统计员工查看列表 class ReportView(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='viewed_reports' ) report = models.ForeignKey( ReportDetails, on_delete=models.CASCADE, related_name='viewed_by' ) viewed_at = models.DateTimeField(auto_now_add=True) class Meta: # 限制同一用户不可重复记录查看同一报告(可按需移除) unique_together = ('user', 'report') def __str__(self): return f"{self.user.email} viewed {self.report.title}"
常用查询示例
- 获取某公司的所有员工:
company.employees.all() - 获取某公司的所有报告:
company.reports.all() - 获取某员工创建的所有报告:
user.created_reports.all() - 获取某员工查看过的所有报告:
user.viewed_reports.all() - 实现公司内员工查看任意报告:在视图层判断当前用户的
company属性与报告的company属性是否一致即可
内容的提问来源于stack exchange,提问作者im_up
相关产品推荐
相关产品推荐

