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

Django外卖APP多用户类型差异化登录注册实现咨询

多用户类型Django外卖应用实现方案

核心思路

Django仅支持配置一个AUTH_USER_MODEL,因此不能为三类用户分别定义独立的自定义用户模型。最优方案是单主用户模型+一对一关联类型扩展表:

  • 主用户模型继承AbstractUser,通过user_type字段区分顾客、商家、骑手
  • 每个用户类型创建一对一关联模型,存储专属字段(如顾客的手机号、商家的企业邮箱)
  • 自定义认证后端,适配不同用户类型的登录方式(OTP/邮箱密码)
  • 拆分视图、URL和权限控制,实现各类型用户的独立流程

具体实现步骤&代码示例

1. 定义主用户模型

在单独的users应用中创建核心用户模型,取消默认用户名,用邮箱/手机号作为登录标识:

# users/models.py
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models
from django.utils.translation import gettext_lazy as _

class UserManager(BaseUserManager):
    def create_user(self, email=None, phone=None, password=None, **extra_fields):
        if not email and not phone:
            raise ValueError("必须填写邮箱或手机号")
        if email:
            email = self.normalize_email(email)
        user = self.model(email=email, phone=phone, **extra_fields)
        if password:
            user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        return self.create_user(email=email, password=password, **extra_fields)

class User(AbstractUser):
    class UserType(models.TextChoices):
        CUSTOMER = 'CUSTOMER', _('顾客')
        SELLER = 'SELLER', _('商家')
        DRIVER = 'DRIVER', _('骑手')

    username = None  # 禁用默认用户名字段
    email = models.EmailField(_('邮箱'), unique=True, blank=True, null=True)
    phone = models.CharField(_('手机号'), max_length=15, unique=True, blank=True, null=True)
    user_type = models.CharField(_('用户类型'), max_length=20, choices=UserType.choices)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

    def __str__(self):
        return self.email or self.phone

2. 各用户类型扩展模型

在拆分的customers、sellers、drivers应用中创建一对一关联模型,存储专属数据:

# customers/models.py
from django.db import models
from users.models import User

class Customer(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
    points = models.IntegerField(default=0)  # 顾客专属字段:积分

    def __str__(self):
        return f"顾客: {self.user.phone}"
# sellers/models.py
from django.db import models
from users.models import User

class Seller(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
    company_name = models.CharField(_('企业名称'), max_length=100)
    business_license = models.CharField(_('营业执照号'), max_length=50)

    def __str__(self):
        return f"商家: {self.company_name}"

3. 自定义认证后端

实现支持OTP和邮箱密码的多类型认证:

# users/backends.py
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from .utils import verify_otp  # 自行实现OTP验证逻辑(如调用短信API)

User = get_user_model()

class MultiUserAuthBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, otp_code=None, **kwargs):
        # 商家登录:邮箱+密码
        if password and username:
            try:
                user = User.objects.get(email=username, user_type=User.UserType.SELLER)
                if user.check_password(password):
                    return user
            except User.DoesNotExist:
                return None
        # 顾客登录:手机号+OTP
        elif otp_code and username:
            try:
                user = User.objects.get(phone=username, user_type=User.UserType.CUSTOMER)
                if verify_otp(user.phone, otp_code):
                    return user
            except User.DoesNotExist:
                return None
        return None

在settings.py配置认证后端:

AUTHENTICATION_BACKENDS = [
    'users.backends.MultiUserAuthBackend',
    'django.contrib.auth.backends.ModelBackend',  # 保留默认后端用于后台管理
]
AUTH_USER_MODEL = 'users.User'

4. 独立登录/注册视图

为不同用户类型编写专属视图:

顾客OTP注册/登录

# customers/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import login
from users.models import User
from .models import Customer
from .utils import send_otp  # 自行实现短信发送逻辑

def customer_register(request):
    if request.method == 'POST':
        phone = request.POST.get('phone')
        # 手机号已存在则跳转至OTP登录
        if User.objects.filter(phone=phone, user_type=User.UserType.CUSTOMER).exists():
            return redirect('customer_otp_login', phone=phone)
        # 创建用户及顾客关联
        user = User.objects.create_user(phone=phone, user_type=User.UserType.CUSTOMER)
        Customer.objects.create(user=user)
        send_otp(phone)
        return redirect('customer_verify_otp', phone=phone)
    return render(request, 'customers/register.html')

def customer_verify_otp(request, phone):
    if request.method == 'POST':
        otp_code = request.POST.get('otp')
        user = User.objects.get(phone=phone, user_type=User.UserType.CUSTOMER)
        if verify_otp(phone, otp_code):
            login(request, user)
            return redirect('customer_dashboard')
        else:
            return render(request, 'customers/verify_otp.html', {'error': '验证码无效', 'phone': phone})
    return render(request, 'customers/verify_otp.html', {'phone': phone})

商家邮箱密码登录/注册

# sellers/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import login, authenticate
from users.models import User
from .models import Seller

def seller_register(request):
    if request.method == 'POST':
        email = request.POST.get('email')
        password = request.POST.get('password')
        company_name = request.POST.get('company_name')
        business_license = request.POST.get('business_license')
        # 创建用户及商家关联
        user = User.objects.create_user(email=email, password=password, user_type=User.UserType.SELLER)
        Seller.objects.create(user=user, company_name=company_name, business_license=business_license)
        login(request, user)
        return redirect('seller_dashboard')
    return render(request, 'sellers/register.html')

def seller_login(request):
    if request.method == 'POST':
        email = request.POST.get('email')
        password = request.POST.get('password')
        user = authenticate(request, username=email, password=password)
        if user and user.user_type == User.UserType.SELLER:
            login(request, user)
            return redirect('seller_dashboard')
        else:
            return render(request, 'sellers/login.html', {'error': '账号或密码错误'})
    return render(request, 'sellers/login.html')

5. 权限控制

编写自定义装饰器,限制页面访问权限:

# users/decorators.py
from django.http import HttpResponseForbidden
from django.contrib.auth.decorators import login_required

def user_type_required(user_type):
    def decorator(view_func):
        @login_required
        def wrapped_view(request, *args, **kwargs):
            if request.user.user_type == user_type:
                return view_func(request, *args, **kwargs)
            else:
                return HttpResponseForbidden("无访问权限")
        return wrapped_view
    return decorator

使用示例:

# customers/views.py
from users.decorators import user_type_required
from users.models import User

@user_type_required(User.UserType.CUSTOMER)
def customer_dashboard(request):
    return render(request, 'customers/dashboard.html')

推荐工具&最佳实践

  • OTP简化:使用django-otp包快速实现OTP生成、验证逻辑,无需手动对接短信API
  • 权限增强:结合django-guardian实现对象级权限控制(如商家仅能管理自己的店铺)
  • 分步注册:商家注册需提交营业执照等资料,用django-formtools实现分步表单
  • API适配:若开发后端API,可基于DRF自定义OTPAuthentication、EmailPasswordAuthentication类,适配多类型用户的接口认证

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 11:04:55