from django.contrib.auth.models import PermissionsMixin, AbstractUser from django.db import models from common.base import BaseEntity # Create your models here. class Role(BaseEntity): """角色模型""" name = models.CharField(max_length=50, unique=True, verbose_name="角色名称") code = models.CharField(max_length=50, unique=True, verbose_name="角色编码") description = models.CharField(max_length=200, blank=True, verbose_name="描述") status = models.BooleanField(default=True, verbose_name="状态") # 直接关联菜单(多对多) menus = models.ManyToManyField( 'Menu', blank=True, related_name='roles', verbose_name="菜单权限" ) class Meta: verbose_name = "角色" verbose_name_plural = verbose_name class Department(BaseEntity): name = models.CharField("部门名称", max_length=100, unique=True) parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, verbose_name="上级部门") order = models.IntegerField("排序", default=0) class Meta: verbose_name = "部门" verbose_name_plural = "部门管理" # 👇 核心:添加部分唯一约束 constraints = [ models.UniqueConstraint( fields=['parent'], condition=models.Q(parent__isnull=True), name='unique_root_department' ) ] def __str__(self): return self.name @property def is_root(self): return self.parent is None class User(AbstractUser, PermissionsMixin, BaseEntity): username = models.CharField("英文名/账号", max_length=30, unique=True) cname = models.CharField("中文名", max_length=30, blank=True) phone = models.CharField(max_length=20, blank=True, verbose_name="手机号") avatar = models.CharField(max_length=255, blank=True, verbose_name="头像") dept = models.ForeignKey('Department', on_delete=models.SET_NULL, null=True, blank=True, verbose_name="所属部门") is_active = models.BooleanField(default=True, verbose_name="状态") # 直接在这里定义多对多关系 👈 roles = models.ManyToManyField( Role, blank=True, related_name='users', verbose_name="角色" ) USERNAME_FIELD = 'username' REQUIRED_FIELDS = [] def __str__(self): return f"{self.cname} ({self.username})" class Menu(BaseEntity): """菜单模型""" MENU_TYPE_CHOICES = [ (0, '目录'), (1, '菜单'), (2, '按钮'), ] name = models.CharField(max_length=50, verbose_name="菜单名称") code = models.CharField(max_length=50, unique=True, verbose_name="菜单编码") path = models.CharField(max_length=200, blank=True, verbose_name="路由路径") component = models.CharField(max_length=200, blank=True, verbose_name="组件路径") icon = models.CharField(max_length=50, blank=True, verbose_name="图标") parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE, verbose_name="父菜单") menu_type = models.IntegerField(choices=MENU_TYPE_CHOICES, default=1, verbose_name="菜单类型") order_num = models.IntegerField(default=0, verbose_name="显示顺序") permission_code = models.CharField(max_length=100, blank=True, verbose_name="权限标识") status = models.BooleanField(default=True, verbose_name="状态") class Meta: verbose_name = "菜单" verbose_name_plural = verbose_name def __str__(self): return self.name