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

如何在Django中关联表并拆分多用户模型实现统一登录

Django多用户类型模型拆分与统一登录实现方案

一、Django中表关联的正确方式

Django提供几种核心关联字段,针对你的场景最常用的是OneToOneField(一对一关联,匹配一个用户对应一个诊所/患者的业务逻辑)和ForeignKey(一对多关联),空值错误的核心解决思路是明确关联的可选性:

  1. OneToOneField(一对一)
    适用于用户与诊所、用户与患者的绑定关系(一个用户只能属于一种专属类型):

    class Clinic(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='clinic_profile')
        # 诊所专属字段...
    
    • on_delete=models.CASCADE:关联用户删除时,对应的诊所数据也会被删除,符合业务逻辑
    • 空值避免:如果要求专属模型必须和用户绑定,不要加null=True;若允许后期补全信息,可添加null=True, blank=True
  2. ForeignKey(一对多)
    适合子管理员管理多个诊所这类场景:

    class Clinic(models.Model):
        sub_admin = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='managed_clinics')
    
    • on_delete=models.SET_NULL:子管理员删除时,诊所的关联字段设为null,需配合null=True使用
  3. 空值错误常见原因与解决

    • 原因:创建专属模型时未指定关联的User实例,或关联字段未设置null=True却未强制赋值
    • 解决:创建Clinic/Patient前先创建对应的User;或通过Django信号在User创建时自动生成关联的专属模型(适合注册时直接指定用户类型的场景)

二、多用户类型模型拆分与统一登录实现

核心方案是自定义基础用户模型+一对一关联专属模型,复用Django自带认证系统实现统一登录:

步骤1:重构模型

基础用户模型(继承AbstractUser)

存放所有用户共有的字段和类型标识:

import django
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    # 覆盖AbstractUser的email字段,设置唯一约束
    email = models.EmailField(unique=True, max_length=254, verbose_name='email address')
    # 用户类型标识
    is_subadmin = models.BooleanField(default=False, verbose_name='子管理员')
    is_clinic = models.BooleanField(default=False, verbose_name='诊所用户')
    is_patient = models.BooleanField(default=False, verbose_name='患者用户')
    # 公共字段
    token = models.CharField(max_length=120, null=True, blank=True)
    forgot_token = models.CharField(max_length=120, null=True, blank=True)
    updated = models.DateTimeField(auto_now=True)
    published = models.DateTimeField(default=django.utils.timezone.now)

    # 指定登录用的字段(默认是username,这里改为email)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

诊所专属模型

一对一关联基础用户模型:

class Clinic(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='clinic_profile')
    clnc_name = models.CharField(max_length=255)
    clnc_phone = models.BigIntegerField(null=True, blank=True)
    clnc_contact_name = models.CharField(max_length=255)
    clnc_job_title = models.CharField(max_length=255)
    clnc_address_line1 = models.CharField(max_length=255)
    clnc_address_line2 = models.CharField(max_length=255, blank=True)
    clnc_county = models.CharField(max_length=255)
    clnc_postcode = models.CharField(max_length=255)
    clnc_town_city = models.CharField(max_length=255)
    clnc_created_at = models.DateTimeField(auto_now_add=True)
    clnc_status = models.BooleanField(default=False)

患者专属模型

一对一关联基础用户模型:

class Patient(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='patient_profile')
    punique_number = models.CharField(max_length=20)
    pname = models.CharField(max_length=255)
    patient_address = models.TextField(max_length=500)
    pphone_number = models.IntegerField(null=True, blank=True)
    phome_number = models.IntegerField(null=True, blank=True)
    pwork_number = models.IntegerField(null=True, blank=True)
    pdob = models.CharField(max_length=255, null=True, blank=True)
    relative_name = models.CharField(max_length=255, null=True, blank=True)
    relative_phone = models.CharField(max_length=255, null=True, blank=True)
    relation = models.CharField(max_length=255, null=True, blank=True)
    pgender = models.CharField(max_length=20)
    pcreated_at = models.DateTimeField(auto_now_add=True)
    ptnt_status = models.BooleanField(default=False)

步骤2:配置认证系统

在settings.py中指定自定义用户模型:

AUTH_USER_MODEL = '你的应用名.User'  # 替换为实际的app名称

步骤3:统一登录实现

直接使用Django认证逻辑,登录后通过用户的is_*字段判断类型并跳转:

from django.contrib.auth import authenticate, login
from django.shortcuts import redirect, render

def user_login(request):
    if request.method == 'POST':
        email = request.POST['email']
        password = request.POST['password']
        # 用邮箱和密码认证用户
        user = authenticate(request, username=email, password=password)
        if user is not None:
            login(request, user)
            # 根据用户类型跳转对应页面
            if user.is_superuser:
                return redirect('superadmin_dashboard')
            elif user.is_subadmin:
                return redirect('subadmin_dashboard')
            elif user.is_clinic:
                return redirect('clinic_dashboard')
            elif user.is_patient:
                return redirect('patient_dashboard')
        else:
            return render(request, 'login.html', {'error': '邮箱或密码错误'})
    return render(request, 'login.html')

步骤4:用户注册示例(以诊所为例)

注册时同步创建基础用户和专属模型:

def register_clinic(request):
    if request.method == 'POST':
        # 创建基础用户
        user = User.objects.create_user(
            email=request.POST['email'],
            username=request.POST['username'],
            password=request.POST['password']
        )
        user.is_clinic = True
        user.save()
        # 创建诊所专属数据
        Clinic.objects.create(
            user=user,
            clnc_name=request.POST['clnc_name'],
            clnc_contact_name=request.POST['clnc_contact_name'],
            # 其他诊所字段...
        )
        return redirect('login')
    return render(request, 'register_clinic.html')

注意事项

  • 超级管理员直接使用Django默认的is_superuser字段,无需额外模型
  • 访问用户专属数据时,可通过request.user.clinic_profile或request.user.patient_profile获取(依赖模型中设置的related_name)
  • 迁移数据库前需清空旧的用户表数据,或做好数据迁移适配

内容的提问来源于stack exchange,提问作者Tejinder Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:24:55