Become a sponsor

核心基类体系由四大基类组成,覆盖从数据模型到请求校验再到响应输出的完整链路。子类只需声明差异点,即可获得标准化的增删改查能力。
BaseModel (application/models.py)
|
+-- 通用字段: id / create_user / create_time / update_user / update_time / is_delete
+-- 字段顺序重排: class_prepared 信号
|
v
Example(BaseModel) <-- 业务模型继承
PermissionRequired (middleware/permission_middleware.py)
|
+-- RBAC权限验证: has_permission()
+-- 超级管理员放行: user_id == 1
+-- 无权限处理: handle_no_permission()
|
v
ExamplePageView(PermissionRequired, View) <-- 视图类混入
check_login (middleware/login_middleware.py)
|
+-- JWT认证: parse_payload()
+-- 忽略URL: /login, /captcha
+-- 认证失败: R.failed(code=401)
|
v
@method_decorator(check_login, name="get") <-- 视图方法装饰器
R (utils/R.py)
|
+-- ok(): 成功响应
+-- failed(): 失败响应
+-- response(): 通用响应
|
v
JsonResponse <-- Django HTTP响应文件位置:application/models.py
为所有业务模型提供统一的通用字段和字段顺序管理,消除重复定义。
class BaseModel(models.Model):
"""
基础模型抽象类
==============
功能:提供所有模型的公共字段,如主键、创建人、创建时间等
特点:定义为抽象类,不会在数据库中创建对应的表
用途:被其他业务模型继承,实现字段复用
继承此基类的模型会自动拥有以下6个公共字段:
- id: 自增主键
- create_user: 创建人
- create_time: 创建时间
- update_user: 更新人
- update_time: 更新时间
- is_delete: 逻辑删除标识
注意:公共字段会自动排列在业务字段之后,使数据库表结构更清晰
"""
# ==================== 公共字段定义 ====================
# 主键ID字段
# 自增主键,每个模型的唯一标识符
id = models.AutoField(
auto_created=True, # 自动创建主键字段
primary_key=True, # 设置为主键
serialize=False, # 不参与序列化(避免在API响应中暴露)
verbose_name='主键ID', # 字段中文名称
db_comment='主键ID' # 数据库字段注释
)
# 创建人字段
# 记录数据由谁创建,通常关联用户表的用户名或ID
# 例如:admin, zhangsan, system
create_user = models.CharField(
null=True, # 数据库允许为NULL
max_length=50, # 最大长度50个字符
default=None, # 默认值为None
db_default=None, # 数据库层默认值
verbose_name='创建人', # 字段中文名称
db_comment='创建人' # 数据库字段注释
)
# 创建时间字段
# 自动记录数据创建的时间戳
# auto_now_add: 仅在创建时自动设置当前时间,后续更新不会改变
create_time = models.DateTimeField(
null=True, # 数据库允许为NULL
auto_now_add=True, # 创建时自动设为当前时间
verbose_name="创建时间", # 字段中文名称
max_length=11, # 时间戳长度
db_comment='创建时间' # 数据库字段注释
)
# 更新人字段
# 记录数据最后由谁更新,通常关联用户表的用户名或ID
# null=True: 允许数据库为NULL(新创建记录时可能没有更新人)
update_user = models.CharField(
null=True, # 数据库允许为NULL
max_length=50, # 最大长度50个字符
default=None, # 默认值为None
db_default=None, # 数据库层默认值
verbose_name='更新人', # 字段中文名称
db_comment='更新人' # 数据库字段注释
)
# 更新时间字段
# 自动记录数据最后更新的时间戳
# auto_now: 每次保存(更新)时自动设为当前时间
update_time = models.DateTimeField(
null=True, # 数据库允许为NULL
auto_now=True, # 每次保存时自动更新为当前时间
verbose_name="更新时间", # 字段中文名称
max_length=11, # 时间戳长度
db_comment='更新时间' # 数据库字段注释
)
# 逻辑删除标识
# 用于软删除,避免物理删除数据造成的数据丢失
# 0:正常(默认值)| 1:已删除
is_delete = models.BooleanField(
default=0, # 默认为0,表示正常
db_default=0, # 数据库层默认值
verbose_name="逻辑删除", # 字段中文名称
db_comment='逻辑删除:0-正常 1-已删除' # 数据库字段注释
)
class Meta:
"""
元数据配置
"""
# 定义为抽象模型类
# abstract = True 表示这个模型是抽象的,不会在数据库中创建对应的表
# 子类模型会继承这些字段,并在各自对应的表中创建这些列
abstract = True| 字段 | 类型 | 说明 |
|---|---|---|
id | AutoField, PK | 主键自增 |
create_user | CharField(50) | 创建人(用户名) |
create_time | DateTimeField | 创建时间(auto_now_add) |
update_user | CharField(50) | 更新人(用户名) |
update_time | DateTimeField | 更新时间(auto_now) |
is_delete | BooleanField | 软删除标识:0=正常,1=已删除 |
通过 class_prepared 信号,自动将公共字段移到业务字段之后:
BASE_FIELD_NAMES = ['id', 'create_user', 'create_time',
'update_user', 'update_time', 'is_delete']
def _reorder_fields(sender, **kwargs):
"""信号处理函数:重排模型字段顺序"""
if not hasattr(sender, '_meta') or sender._meta.abstract:
return
# 分离公共字段和业务字段
base_fields = []
business_fields = []
for field in sender._meta.local_fields:
if field.name in BASE_FIELD_NAMES:
base_fields.append(field)
else:
business_fields.append(field)
# 重新排列:业务字段在前,公共字段在后
sender._meta.local_fields = business_fields + base_fields
# 注册信号处理器
class_prepared.connect(_reorder_fields)from application.models import BaseModel
from utils.common import get_table_name
class Example(BaseModel):
"""案例模型类"""
name = models.CharField(max_length=100, verbose_name="案例名称")
status = models.IntegerField(verbose_name="状态")
class Meta:
db_table = get_table_name('example')
ordering = ("sort",)继承 BaseModel 后,Example 模型自动拥有 6 个公共字段,无需手动定义。
文件位置:middleware/login_middleware.py
提供基于 JWT 的用户登录状态验证装饰器,保护需要登录才能访问的视图函数。
from utils import R
from utils.jwt import parse_payload
def check_login(func):
"""JWT登录认证装饰器"""
def wrapper(request, *args, **kwargs):
# 定义不需要认证的URL列表
ignoreURL = ['/login', '/captcha']
# 判断当前请求是否需要验证登录
if request.path not in ignoreURL:
# 从请求头中获取token值
access_token = request.headers['Authorization']
# 移除"Bearer "前缀
access_token = access_token.replace('Bearer ', "")
# JWT解密验证
result = parse_payload(access_token)
# token验证失败处理
code = result['code']
if code != 0:
return R.failed(code=401, msg=result['msg'])
# 认证通过,继续执行原视图函数
return func(request, *args, **kwargs)
return wrapper1. 检查请求路径是否在忽略列表中
+-- 是 -> 直接执行视图函数
+-- 否 -> 继续验证
2. 从请求头获取 Authorization 值
+-- 移除 "Bearer " 前缀
+-- 获取纯净的 token 字符串
3. 调用 parse_payload() 验证 token
+-- 检查 Token 是否在黑名单中
+-- 解密验证签名和有效期
+-- 返回验证结果
4. 根据验证结果决定是否放行
+-- code == 0 -> 放行
+-- code != 0 -> 返回 401 错误from django.utils.decorators import method_decorator
from middleware.login_middleware import check_login
# 在类视图中使用
@method_decorator(check_login, name="get")
class ExamplePageView(View):
def get(self, request):
pass
# 在函数视图中使用
@check_login
def user_info(request):
pass文件位置:middleware/permission_middleware.py
提供基于 RBAC 的细粒度权限控制,通过检查用户是否拥有指定权限节点来决定是否允许访问。
from django.contrib.auth.mixins import PermissionRequiredMixin
from utils import R
from utils.security import get_user_id
class PermissionRequired(PermissionRequiredMixin):
"""自定义权限控制混入类"""
def has_permission(self):
"""检查用户是否拥有所需权限"""
# 获取所需权限节点列表
permissions = self.get_permission_required()
# 获取当前登录用户ID
user_id = get_user_id(self.request)
# 超级管理员(ID=1)自动放行
if user_id and user_id != 1:
from application.menu import services
# 获取用户权限节点
permission_list = services.get_user_permissions(user_id)
# 遍历所需权限列表
for permission in permissions:
if permission not in permission_list:
return False
return True
def handle_no_permission(self):
"""无权限访问时的处理函数"""
return R.failed("暂无操作权限", 401)1. 获取视图所需的权限节点列表
permission_required = ("sys:example:page",)
2. 获取当前登录用户ID
user_id = get_user_id(request)
3. 判断是否为超级管理员
+-- user_id == 1 -> 直接放行
+-- user_id != 1 -> 继续验证
4. 获取用户的实际权限列表
permission_list = services.get_user_permissions(user_id)
5. 比对所需权限是否在用户权限列表中
+-- 全部存在 -> 放行
+-- 有缺失 -> 返回 401 错误# application/menu/services.py
def get_user_permissions(user_id):
"""获取用户权限节点列表"""
if user_id == 1:
# 超级管理员拥有所有权限
menu_list = Menu.objects.filter(
is_delete=False, type=1, status=0
).values()
return [item['permission'] for item in menu_list]
else:
# 普通用户从角色关联中获取权限
sql = '''
SELECT m.* FROM django_menu AS m
INNER JOIN django_role_menu AS rm ON m.id=rm.menu_id
INNER JOIN django_user_role AS ur ON ur.role_id=rm.role_id
WHERE ur.user_id=%s
AND (m.type=1 OR (m.type=0 AND m.permission!=''))
AND m.status=0 AND m.is_delete=0
'''
menu_list = Menu.objects.raw(sql, [user_id])
return [item.permission for item in menu_list]from django.views import View
from middleware.permission_middleware import PermissionRequired
class ExamplePageView(PermissionRequired, View):
"""案例分页数据查询视图"""
# 声明所需权限节点
permission_required = ("sys:example:page",)
def get(self, request):
pass文件位置:utils/R.py
提供系统统一的 JSON 响应格式,确保所有 API 接口返回格式一致。
{
"code": 0, // 状态码:0-成功,1-失败,401-未授权
"data": {}, // 响应数据
"msg": "操作成功", // 提示信息
"ok": true // 操作是否成功
}from django.http import JsonResponse
def ok(data=None, msg="操作成功", code=0, **kwargs):
"""生成成功响应"""
response_body = {
"code": code,
"data": data,
"msg": msg,
"ok": True
}
if kwargs:
response_body.update(kwargs)
return JsonResponse(data=response_body)
def failed(msg="操作失败", code=1, data=None, **kwargs):
"""生成失败响应"""
response_body = {
"code": code,
"data": data,
"msg": msg,
"ok": False
}
if kwargs:
response_body.update(kwargs)
return JsonResponse(data=response_body)
def response(data=None, msg="操作成功", code=0, success=True, **kwargs):
"""通用响应生成器"""
response_body = {
"code": code,
"data": data,
"msg": msg,
"ok": success
}
if kwargs:
response_body.update(kwargs)
return JsonResponse(data=response_body)from utils import R
# 成功响应
return R.ok(data={"id": 1, "name": "张三"})
# {"code": 0, "data": {"id": 1, "name": "张三"}, "msg": "操作成功", "ok": true}
# 分页响应
return R.ok(data=page_data, total=100, pages=10)
# {"code": 0, "data": {...}, "msg": "操作成功", "ok": true, "total": 100, "pages": 10}
# 失败响应
return R.failed(msg="用户名或密码错误")
# {"code": 1, "data": null, "msg": "用户名或密码错误", "ok": false}
# 认证失败响应
return R.failed(code=401, msg="token已失效")
# {"code": 401, "data": null, "msg": "token已失效", "ok": false}class Codes:
"""常用状态码定义"""
SUCCESS = 0
FAILED = 1
UNAUTHORIZED = 401 # 未认证
FORBIDDEN = 403 # 无权限
NOT_FOUND = 404 # 资源不存在
VALIDATE_ERROR = 422 # 验证错误
SERVER_ERROR = 500 # 服务器错误HTTP Request
|
v
@check_login (认证装饰器)
|
+-- 验证JWT Token
+-- 失败 -> R.failed(code=401)
|
v
PermissionRequired (权限混入类)
|
+-- 验证权限节点
+-- 失败 -> R.failed("暂无操作权限", 401)
|
v
View.get/post (视图方法)
|
+-- 调用 Service 函数
|
v
Service函数
|
+-- 使用 BaseModel 子类进行数据库操作
+-- 成功 -> R.ok(data=...)
+-- 失败 -> R.failed(msg=...)
|
v
JsonResponse (HTTP响应)| 基类 | 文件位置 | 核心职责 |
|---|---|---|
BaseModel | application/models.py | 提供6个通用字段,字段顺序重排 |
check_login | middleware/login_middleware.py | JWT认证,验证登录状态 |
PermissionRequired | middleware/permission_middleware.py | RBAC权限控制,细粒度权限验证 |
R | utils/R.py | 统一响应格式,JSON响应生成 |