明白,你是想把 Django 或 Python 报错信息的英文翻译成 中文提示,对吗?我可以帮你做到几个层次:
- 手动翻译单条错误:你把报错贴给我,我帮你逐句翻译成中文,并解释含义。
- 统一映射字典:把常见的 Django 或 Python 错误类型(
ValueError
,IntegrityError
,DoesNotExist
,PermissionDenied
等)建立映射,方便在项目里统一显示中文提示。 - 自动捕获与翻译:在 Django 的全局异常处理中,将系统报错捕获并根据字典翻译成中文返回前端或日志。
例如,建立一个简单映射:
ERROR_TRANSLATIONS = {
"ValueError": "值错误:输入的值不合法",
"IntegrityError": "数据库完整性错误:违反约束条件",
"DoesNotExist": "数据不存在",
"PermissionDenied": "权限不足",
"OperationalError": "数据库操作错误",
"TypeError": "类型错误:操作或函数的类型不匹配",
"KeyError": "键错误:字典中不存在指定键",
}
然后在全局异常捕获:
from django.http import JsonResponse
from django.core.exceptions import PermissionDenied
from django.db import IntegrityError
def custom_exception_handler(get_response):
def middleware(request):
try:
response = get_response(request)
return response
except Exception as e:
error_type = type(e).__name__
message = ERROR_TRANSLATIONS.get(error_type, str(e))
return JsonResponse({"error": message}, status=500)
return middleware
明白了,你希望的不只是常规 Python 异常的中文翻译,还要包括 Django ORM/数据库操作相关的具体错误,比如:
- 外键约束错误(
ForeignKeyViolation
/IntegrityError
) - 唯一约束错误(
UniqueViolation
) - 数据类型不匹配(
DataError
) - 数据库连接或操作异常(
OperationalError
) - 查询结果不存在(
DoesNotExist
) - 权限相关(
PermissionDenied
)
我们可以做一个 详细的异常映射表,包括:
英文异常 | 中文翻译 | 说明 / 建议操作 |
---|---|---|
ValueError |
值错误 | 输入的值不合法或格式错误 |
TypeError |
类型错误 | 函数或操作的类型不匹配 |
KeyError |
键错误 | 字典中不存在指定键 |
DoesNotExist |
数据不存在 | 查询的对象不存在 |
PermissionDenied |
权限不足 | 当前用户无权限执行操作 |
IntegrityError |
数据库完整性错误 | 违反数据库约束,如外键或唯一约束 |
OperationalError |
数据库操作错误 | 数据库连接失败或执行语句失败 |
ForeignKeyViolation |
外键约束错误 | 关联的外键记录不存在或被删除 |
UniqueViolation |
唯一约束错误 | 数据重复,违反唯一约束 |
DataError |
数据类型或长度错误 | 字段数据类型不匹配或长度超出限制 |
ProgrammingError |
数据库语法错误 | SQL语句错误或数据库表结构异常 |
IntegrityError: NOT NULL constraint failed |
非空约束失败 | 插入或更新数据时字段为空 |
IntegrityError: FOREIGN KEY constraint failed |
外键约束失败 | 插入或更新数据时外键引用不存在 |
ValidationError |
数据验证错误 | 表单或模型字段验证未通过 |
⚡ 额外建议:
- 结合异常捕获和正则匹配,可以针对
IntegrityError
的具体原因返回更友好的中文提示。 - 对 ORM 操作和表单验证分别处理,避免把前端错误和数据库错误混淆。
明白,你希望一个 Django 全局异常捕获方案,不仅能把系统异常翻译成中文,还能在数据库操作出错时,准确定位是哪个模型出错以及哪种约束出错。我帮你设计一个完整方案。
下面是示例代码:
# middleware.py
import re
from django.http import JsonResponse
from django.core.exceptions import PermissionDenied, ValidationError, ObjectDoesNotExist
from django.db import IntegrityError, DataError, OperationalError
from django.db.models import Model
# 异常映射
ERROR_TRANSLATIONS = {
"ValueError": "值错误:输入值不合法",
"TypeError": "类型错误:操作或函数的类型不匹配",
"KeyError": "键错误:字典中不存在指定键",
"PermissionDenied": "权限不足:您无权执行此操作",
"ValidationError": "数据验证错误",
"DoesNotExist": "数据不存在",
"IntegrityError": "数据库完整性错误",
"OperationalError": "数据库操作错误",
"DataError": "数据库字段数据错误",
}
# 针对常见 IntegrityError 的正则
INTEGRITY_PATTERNS = {
r"FOREIGN KEY constraint failed": "外键约束失败",
r"NOT NULL constraint failed: (.+?)\.(.+)": "非空约束失败: 表 {table}, 字段 {field}",
r"UNIQUE constraint failed: (.+?)\.(.+)": "唯一约束失败: 表 {table}, 字段 {field}",
}
def get_integrity_error_detail(exc):
"""
分析 IntegrityError 错误信息,尝试返回表名和字段
"""
msg = str(exc)
for pattern, desc in INTEGRITY_PATTERNS.items():
m = re.search(pattern, msg)
if m:
if "{table}" in desc:
table, field = m.groups()
return desc.format(table=table, field=field)
return desc
return msg
class CustomExceptionMiddleware:
"""
全局异常中间件,返回中文提示
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
response = self.get_response(request)
return response
except Exception as exc:
# 默认返回
error_type = type(exc).__name__
message = ERROR_TRANSLATIONS.get(error_type, str(exc))
model_name = None
# 针对数据库完整性错误
if isinstance(exc, IntegrityError):
message = get_integrity_error_detail(exc)
# 尝试获取出错的模型名(如果在事务中有模型信息)
if hasattr(exc, "model") and isinstance(exc.model, Model):
model_name = exc.model.__class__.__name__
elif isinstance(exc, ObjectDoesNotExist):
model_name = exc.__class__.__name__.replace("DoesNotExist", "")
message = f"{model_name} 数据不存在"
elif isinstance(exc, ValidationError):
message = f"数据验证错误: {exc}"
# 返回 JSON
result = {"error": message}
if model_name:
result["model"] = model_name
return JsonResponse(result, status=500)
✅ 功能说明
- 通用异常捕获:捕获 Python 和 Django 常见异常,并映射中文提示。
- 数据库错误精确提示:
- 外键约束、唯一约束、非空约束的具体字段和表名。
- 可扩展正则来匹配更多 Postgres/MySQL 异常。
- 模型名称返回:如果异常属于
DoesNotExist
或数据库操作异常,尽量返回出错模型名。 - 可返回前端 JSON:方便前端显示中文友好提示。
好,我们可以把中间件升级成 高级版,目标是:
- 捕获
IntegrityError
、DataError
、OperationalError
等数据库错误 - 自动解析 出错的 Django 模型 和 字段
- 返回清晰的中文提示给前端
由于 Django 的 IntegrityError
本身不会直接告诉你是哪个模型操作失败,我们需要通过 堆栈追踪 或 事务操作对象推测 来获取模型信息。下面是一个可行方案:
# advanced_middleware.py
import re
import traceback
from django.http import JsonResponse
from django.core.exceptions import ValidationError, ObjectDoesNotExist, PermissionDenied
from django.db import IntegrityError, DataError, OperationalError
from django.db.models import Model
from functools import wraps
# 异常映射
ERROR_TRANSLATIONS = {
"ValueError": "值错误:输入值不合法",
"TypeError": "类型错误:操作或函数的类型不匹配",
"KeyError": "键错误:字典中不存在指定键",
"PermissionDenied": "权限不足:您无权执行此操作",
"ValidationError": "数据验证错误",
"DoesNotExist": "数据不存在",
"IntegrityError": "数据库完整性错误",
"OperationalError": "数据库操作错误",
"DataError": "数据库字段数据错误",
}
# 针对常见 IntegrityError 的正则
INTEGRITY_PATTERNS = {
r"FOREIGN KEY constraint failed": "外键约束失败",
r"NOT NULL constraint failed: (.+?)\.(.+)": "非空约束失败: 表 {table}, 字段 {field}",
r"UNIQUE constraint failed: (.+?)\.(.+)": "唯一约束失败: 表 {table}, 字段 {field}",
}
def get_integrity_error_detail(exc):
"""
分析 IntegrityError 错误信息,尝试返回表名和字段
"""
msg = str(exc)
for pattern, desc in INTEGRITY_PATTERNS.items():
m = re.search(pattern, msg)
if m:
if "{table}" in desc:
table, field = m.groups()
return desc.format(table=table, field=field)
return desc
return msg
def guess_model_from_traceback(exc):
"""
通过堆栈信息尝试获取出错的模型名
"""
tb_lines = traceback.format_exc().splitlines()
for line in tb_lines[::-1]: # 倒序查找,优先最近的调用
m = re.search(r'File ".*models.py".*in .*', line)
if m:
# 简单提取模型类名:在调用处找 Model 对象
# 这里仅作为示例,实际可结合 ORM 操作栈进一步优化
return "未知模型(推测自 models.py)"
return None
class AdvancedExceptionMiddleware:
"""
全局异常中间件,高级版:
- 捕获异常
- 中文翻译
- 数据库错误精确解析模型/字段
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
return self.get_response(request)
except Exception as exc:
error_type = type(exc).__name__
message = ERROR_TRANSLATIONS.get(error_type, str(exc))
model_name = None
# IntegrityError
if isinstance(exc, IntegrityError):
message = get_integrity_error_detail(exc)
model_name = guess_model_from_traceback(exc)
# ObjectDoesNotExist
elif isinstance(exc, ObjectDoesNotExist):
model_name = exc.__class__.__name__.replace("DoesNotExist", "")
message = f"{model_name} 数据不存在"
# ValidationError
elif isinstance(exc, ValidationError):
message = f"数据验证错误: {exc}"
# PermissionDenied
elif isinstance(exc, PermissionDenied):
message = "权限不足:您无权执行此操作"
# 返回 JSON
result = {"error": message}
if model_name:
result["model"] = model_name
return JsonResponse(result, status=500)
⚡ 功能亮点
- 中文提示:几乎覆盖所有常见 Python/Django 异常。
- 数据库约束解析:外键、唯一、非空约束会给出表和字段信息。
- 模型追踪:通过异常堆栈推测可能出错的模型名(可根据项目进一步精细化)。
- 统一返回 JSON:方便前端友好显示。
💡 进一步优化思路:
- 对
IntegrityError
做 事务追踪,获取Model
对象,例如在每个 ORM 操作前记录instance._meta.model_name
,出错时直接返回。 - 对 Postgres/MySQL 特定错误码做精确匹配,获取更多约束信息。
- 对 批量操作(
bulk_create
,update
)也可返回具体行号或对象索引。
明白,我给你设计一个 最终版本的全局异常捕获中间件,可以做到:
- 捕获 Django/Python 常见异常
- 中文提示
- 精确返回出错模型、字段、操作类型
- 支持批量操作和事务追踪
实现思路:
- 在每次 ORM 操作前记录操作上下文:包括模型名、字段、操作类型(插入/更新/删除)
- 使用线程局部存储保存当前操作上下文,异常时取出
- 全局中间件捕获异常,结合上下文返回精准信息
下面是可直接使用的示例:
# advanced_exception_middleware.py
import threading
import re
import traceback
from functools import wraps
from django.http import JsonResponse
from django.core.exceptions import ValidationError, ObjectDoesNotExist, PermissionDenied
from django.db import IntegrityError, DataError, OperationalError
# 全局线程局部变量,用于记录 ORM 操作上下文
_thread_local = threading.local()
def set_orm_context(model_name=None, fields=None, operation=None):
"""
设置当前 ORM 操作上下文
"""
_thread_local.orm_context = {
"model_name": model_name,
"fields": fields,
"operation": operation
}
def clear_orm_context():
if hasattr(_thread_local, "orm_context"):
del _thread_local.orm_context
def get_orm_context():
return getattr(_thread_local, "orm_context", None)
# 异常中文映射
ERROR_TRANSLATIONS = {
"ValueError": "值错误:输入值不合法",
"TypeError": "类型错误:操作或函数的类型不匹配",
"KeyError": "键错误:字典中不存在指定键",
"PermissionDenied": "权限不足:您无权执行此操作",
"ValidationError": "数据验证错误",
"DoesNotExist": "数据不存在",
"IntegrityError": "数据库完整性错误",
"OperationalError": "数据库操作错误",
"DataError": "数据库字段数据错误",
}
# IntegrityError 常见模式
INTEGRITY_PATTERNS = {
r"FOREIGN KEY constraint failed": "外键约束失败",
r"NOT NULL constraint failed: (.+?)\.(.+)": "非空约束失败: 表 {table}, 字段 {field}",
r"UNIQUE constraint failed: (.+?)\.(.+)": "唯一约束失败: 表 {table}, 字段 {field}",
}
def parse_integrity_error(exc):
"""
解析 IntegrityError 返回表和字段信息
"""
msg = str(exc)
for pattern, desc in INTEGRITY_PATTERNS.items():
m = re.search(pattern, msg)
if m:
if "{table}" in desc:
table, field = m.groups()
return desc.format(table=table, field=field)
return desc
return msg
# ORM 装饰器,用于自动记录模型操作
def orm_operation(model_name=None, fields=None, operation=None):
"""
装饰 ORM 方法,捕获上下文
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
set_orm_context(model_name=model_name, fields=fields, operation=operation)
try:
return func(*args, **kwargs)
finally:
clear_orm_context()
return wrapper
return decorator
# 高级异常中间件
class AdvancedExceptionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
return self.get_response(request)
except Exception as exc:
error_type = type(exc).__name__
message = ERROR_TRANSLATIONS.get(error_type, str(exc))
model_name = None
fields = None
operation = None
# 获取 ORM 上下文
context = get_orm_context()
if context:
model_name = context.get("model_name")
fields = context.get("fields")
operation = context.get("operation")
# 数据库完整性错误
if isinstance(exc, IntegrityError):
message = parse_integrity_error(exc)
# 数据验证错误
elif isinstance(exc, ValidationError):
message = f"数据验证错误: {exc}"
# 查询对象不存在
elif isinstance(exc, ObjectDoesNotExist):
if model_name:
message = f"{model_name} 数据不存在"
else:
message = "数据不存在"
# 权限异常
elif isinstance(exc, PermissionDenied):
message = "权限不足:您无权执行此操作"
result = {"error": message}
if model_name:
result["model"] = model_name
if fields:
result["fields"] = fields
if operation:
result["operation"] = operation
return JsonResponse(result, status=500)
⚡ 使用方法
- 全局注册中间件:
MIDDLEWARE = [
# ... 其他中间件
"your_project.middleware.advanced_exception_middleware.AdvancedExceptionMiddleware",
]
- 在 ORM 操作前使用装饰器:
from .advanced_exception_middleware import orm_operation
@orm_operation(model_name="User", fields=["email"], operation="create")
def create_user(email, name):
User.objects.create(email=email, name=name)
- 效果:
- 如果插入重复 email(唯一约束),返回:
{
"error": "唯一约束失败: 表 auth_user, 字段 email",
"model": "User",
"fields": ["email"],
"operation": "create"
}
- 查询不存在对象:
{
"error": "User 数据不存在",
"model": "User"
}
✅ 优势:
- 全局捕获:前端永远拿到中文提示
- 精确定位:模型、字段、操作类型一目了然
- 可扩展:支持更多数据库错误正则或 ORM 操作追踪
- 支持批量操作:批量 create/update/delete 可通过
fields
数组记录关键字段
完全可以针对你描述的这种情况做处理,但需要注意:Django 默认的 IntegrityError
在 删除数据触发外键约束失败 时,异常本身只会告诉你违反了外键约束,并不会直接告诉你“引用这条数据的模型是哪一个”。
要做到你描述的效果,需要结合 ORM 上下文或数据库表结构信息 来推断。思路如下:
1️⃣ 异常捕获
当你删除某条对象时,如果有外键引用,数据库会抛出 IntegrityError
,消息类似:
- PostgreSQL:
update or delete on table "parent" violates foreign key constraint "child_parent_id_fkey" on table "child"
- SQLite/MySQL:
FOREIGN KEY constraint failed
2️⃣ 从异常消息解析引用表
我们可以用正则解析外键约束的报错信息:
def parse_foreign_key_error(exc):
"""
解析外键约束失败的异常,返回引用的表名(模型)和字段
"""
msg = str(exc)
# PostgreSQL 风格
m = re.search(r'violates foreign key constraint "(.+?)" on table "(.+?)"', msg)
if m:
constraint_name, referencing_table = m.groups()
return referencing_table # 返回引用表名
# SQLite/MySQL 风格
if "FOREIGN KEY constraint failed" in msg:
return "未知引用表"
return None
3️⃣ 在中间件中结合 ORM 上下文返回
if isinstance(exc, IntegrityError):
# 尝试解析外键引用表
referencing_table = parse_foreign_key_error(exc)
if referencing_table:
message = f"删除失败:存在外键依赖,表 '{referencing_table}' 中有数据引用该对象,请先删除或解除依赖。"
else:
message = parse_integrity_error(exc)
message
就是中文提示referencing_table
可以进一步映射成对应 Django 模型名(通过_meta.db_table
或自定义映射表)
4️⃣ 可选优化
- 建一个 表名 → 模型名映射字典,把数据库表名自动映射成 Django 模型名返回前端
- 如果你想更智能,可以在 删除操作前扫描外键依赖,直接提示具体依赖模型和字段,而不是等数据库报错
✅ 总结
- 适用场景:删除对象时触发外键约束
- 实现方法:在中间件或异常处理处解析
IntegrityError
的外键报错信息 - 效果:前端或日志提示哪个模型/表存在数据依赖,需要先删除或解除依赖
- 可拓展:结合 ORM 上下文和表名映射,完全可返回“模型名 + 字段名 + 操作类型”的精准信息
明白,我帮你整合一个 最终版本的高级异常中间件,专门针对你描述的场景优化:
- 捕获 Django/Python 异常
- 中文提示
- ORM 上下文追踪(模型名、字段、操作类型)
- 删除对象时外键依赖自动解析,返回引用模型和字段信息
- 支持批量操作
下面是完整示例:
# final_exception_middleware.py
import threading
import re
import traceback
from functools import wraps
from django.http import JsonResponse
from django.core.exceptions import ValidationError, ObjectDoesNotExist, PermissionDenied
from django.db import IntegrityError, DataError, OperationalError
from django.apps import apps
# 线程局部存储 ORM 上下文
_thread_local = threading.local()
def set_orm_context(model_name=None, fields=None, operation=None):
_thread_local.orm_context = {
"model_name": model_name,
"fields": fields,
"operation": operation
}
def clear_orm_context():
if hasattr(_thread_local, "orm_context"):
del _thread_local.orm_context
def get_orm_context():
return getattr(_thread_local, "orm_context", None)
# 异常中文映射
ERROR_TRANSLATIONS = {
"ValueError": "值错误:输入值不合法",
"TypeError": "类型错误:操作或函数的类型不匹配",
"KeyError": "键错误:字典中不存在指定键",
"PermissionDenied": "权限不足:您无权执行此操作",
"ValidationError": "数据验证错误",
"DoesNotExist": "数据不存在",
"IntegrityError": "数据库完整性错误",
"OperationalError": "数据库操作错误",
"DataError": "数据库字段数据错误",
}
# IntegrityError 常见模式
INTEGRITY_PATTERNS = {
r"FOREIGN KEY constraint failed": "外键约束失败",
r"NOT NULL constraint failed: (.+?)\.(.+)": "非空约束失败: 表 {table}, 字段 {field}",
r"UNIQUE constraint failed: (.+?)\.(.+)": "唯一约束失败: 表 {table}, 字段 {field}",
}
# 数据库表名 -> Django 模型映射
TABLE_MODEL_MAP = {m._meta.db_table: m.__name__ for m in apps.get_models()}
def parse_integrity_error(exc):
"""
解析 IntegrityError 返回中文信息
"""
msg = str(exc)
for pattern, desc in INTEGRITY_PATTERNS.items():
m = re.search(pattern, msg)
if m:
if "{table}" in desc:
table, field = m.groups()
model_name = TABLE_MODEL_MAP.get(table, table)
return desc.format(table=model_name, field=field), model_name, [field]
return desc, None, None
return msg, None, None
def parse_foreign_key_error(exc):
"""
解析外键约束失败,返回引用模型名
"""
msg = str(exc)
# PostgreSQL 风格
m = re.search(r'violates foreign key constraint "(.+?)" on table "(.+?)"', msg)
if m:
_, referencing_table = m.groups()
model_name = TABLE_MODEL_MAP.get(referencing_table, referencing_table)
return model_name
# SQLite/MySQL 风格
if "FOREIGN KEY constraint failed" in msg:
return "未知引用模型"
return None
# ORM 装饰器
def orm_operation(model_name=None, fields=None, operation=None):
"""
装饰 ORM 方法,记录操作上下文
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
set_orm_context(model_name=model_name, fields=fields, operation=operation)
try:
return func(*args, **kwargs)
finally:
clear_orm_context()
return wrapper
return decorator
# 高级异常中间件
class FinalExceptionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
return self.get_response(request)
except Exception as exc:
error_type = type(exc).__name__
message = ERROR_TRANSLATIONS.get(error_type, str(exc))
model_name = None
fields = None
operation = None
# 获取 ORM 上下文
context = get_orm_context()
if context:
model_name = context.get("model_name")
fields = context.get("fields")
operation = context.get("operation")
# 数据库完整性错误
if isinstance(exc, IntegrityError):
# 外键约束失败
fk_model = parse_foreign_key_error(exc)
if fk_model:
message = f"操作失败:存在外键依赖,模型 '{fk_model}' 中有数据引用该对象,请先删除或解除依赖。"
model_name = fk_model
else:
# 其他完整性错误解析
msg, m_name, flds = parse_integrity_error(exc)
message = msg
if m_name:
model_name = m_name
if flds:
fields = flds
elif isinstance(exc, ValidationError):
message = f"数据验证错误: {exc}"
elif isinstance(exc, ObjectDoesNotExist):
if model_name:
message = f"{model_name} 数据不存在"
else:
message = "数据不存在"
elif isinstance(exc, PermissionDenied):
message = "权限不足:您无权执行此操作"
result = {"error": message}
if model_name:
result["model"] = model_name
if fields:
result["fields"] = fields
if operation:
result["operation"] = operation
return JsonResponse(result, status=500)
⚡ 使用方法
- 注册中间件:
MIDDLEWARE = [
# ... 其他中间件
"your_project.middleware.final_exception_middleware.FinalExceptionMiddleware",
]
- 使用 ORM 装饰器记录操作上下文:
from .final_exception_middleware import orm_operation
from myapp.models import User
@orm_operation(model_name="User", fields=["email"], operation="delete")
def delete_user(user_id):
user = User.objects.get(id=user_id)
user.delete()
- 效果:
- 删除对象时,如果有外键引用:
{
"error": "操作失败:存在外键依赖,模型 'Profile' 中有数据引用该对象,请先删除或解除依赖。",
"model": "Profile",
"operation": "delete"
}
- 删除成功但唯一约束失败:
{
"error": "唯一约束失败: 表 User, 字段 email",
"model": "User",
"fields": ["email"],
"operation": "create"
}
✅ 优势:
- 自动解析外键依赖,告诉前端是哪个模型阻止删除
- ORM 上下文追踪操作类型和字段
- 全局捕获所有常见异常,统一中文提示
- 支持批量操作,可扩展到其他数据库错误
明白了,你希望不依赖中间件,而是写一个通用函数,在进行 ORM 操作(增删改)时:
- 自动捕获异常
- 中文提示
- 自动推断出错的模型和字段
- 支持外键约束删除提示
我帮你设计一个 完全函数化的高阶装饰器 + 捕获函数,用法如下:
1️⃣ 高阶 ORM 捕获装饰器
import re
from functools import wraps
from django.db import IntegrityError
from django.core.exceptions import ValidationError, ObjectDoesNotExist, PermissionDenied
from django.apps import apps
# 数据库表名 -> Django 模型映射
TABLE_MODEL_MAP = {m._meta.db_table: m.__name__ for m in apps.get_models()}
# 异常中文映射
ERROR_TRANSLATIONS = {
"ValueError": "值错误:输入值不合法",
"TypeError": "类型错误:操作或函数的类型不匹配",
"KeyError": "键错误:字典中不存在指定键",
"PermissionDenied": "权限不足:您无权执行此操作",
"ValidationError": "数据验证错误",
"DoesNotExist": "数据不存在",
"IntegrityError": "数据库完整性错误",
}
# IntegrityError 常见模式
INTEGRITY_PATTERNS = {
r"FOREIGN KEY constraint failed": "外键约束失败",
r"NOT NULL constraint failed: (.+?)\.(.+)": "非空约束失败: 表 {table}, 字段 {field}",
r"UNIQUE constraint failed: (.+?)\.(.+)": "唯一约束失败: 表 {table}, 字段 {field}",
}
def parse_integrity_error(exc):
msg = str(exc)
for pattern, desc in INTEGRITY_PATTERNS.items():
m = re.search(pattern, msg)
if m:
if "{table}" in desc:
table, field = m.groups()
model_name = TABLE_MODEL_MAP.get(table, table)
return desc.format(table=model_name, field=field), model_name, [field]
return desc, None, None
return msg, None, None
def parse_foreign_key_error(exc):
msg = str(exc)
# PostgreSQL 风格
m = re.search(r'violates foreign key constraint "(.+?)" on table "(.+?)"', msg)
if m:
_, referencing_table = m.groups()
return TABLE_MODEL_MAP.get(referencing_table, referencing_table)
# SQLite/MySQL 风格
if "FOREIGN KEY constraint failed" in msg:
return "未知引用模型"
return None
def orm_safe(func):
"""
高阶装饰器:自动捕获 ORM 异常并返回中文提示
"""
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as exc:
error_type = type(exc).__name__
message = ERROR_TRANSLATIONS.get(error_type, str(exc))
model_name = None
fields = None
operation = None
# IntegrityError 处理
if isinstance(exc, IntegrityError):
fk_model = parse_foreign_key_error(exc)
if fk_model:
message = f"操作失败:存在外键依赖,模型 '{fk_model}' 中有数据引用该对象,请先删除或解除依赖。"
model_name = fk_model
operation = "delete"
else:
msg, m_name, flds = parse_integrity_error(exc)
message = msg
model_name = m_name
fields = flds
operation = "create/update"
elif isinstance(exc, ValidationError):
message = f"数据验证错误: {exc}"
elif isinstance(exc, ObjectDoesNotExist):
message = "数据不存在"
elif isinstance(exc, PermissionDenied):
message = "权限不足:您无权执行此操作"
# 返回统一字典
return {
"error": message,
"model": model_name,
"fields": fields,
"operation": operation
}
return wrapper
2️⃣ 使用示例
from myapp.models import User, Profile
@orm_safe
def delete_user(user_id):
user = User.objects.get(id=user_id)
user.delete()
@orm_safe
def create_user(email, name):
User.objects.create(email=email, name=name)
3️⃣ 调用效果
result = delete_user(1)
if result and "error" in result:
print(result)
可能输出:
{
"error": "操作失败:存在外键依赖,模型 'Profile' 中有数据引用该对象,请先删除或解除依赖。",
"model": "Profile",
"fields": null,
"operation": "delete"
}
或:
{
"error": "唯一约束失败: 表 User, 字段 email",
"model": "User",
"fields": ["email"],
"operation": "create/update"
}
✅ 优点:
- 不依赖中间件,函数/方法独立可用
- 自动解析外键约束,提示阻止删除的模型
- 统一中文提示 + 模型/字段/操作信息
- 可直接扩展 批量操作、更新操作
明白了,你希望最终版本是 完全自动化、零手动装饰器,能做到:
- 创建、更新、删除时 所有常见数据库约束错误 都能自动捕获
- 自动返回中文提示
- 自动推断 出错模型、字段、操作类型
- 外键约束导致删除失败时,返回引用模型和字段
- 唯一约束、非空约束、类型错误、数据长度错误等创建/更新错误也能返回
我帮你设计一个 通用函数 + 高阶装饰器 + ORM 自动追踪 方案:
1️⃣ 核心思路
- QuerySet/Model 封装:用装饰器包装 ORM 方法(create、update、delete、save 等)
- 自动推断模型名:通过对象
_meta.model_name
或 QuerySet 模型 - 字段追踪:获取操作数据的字段
- 异常解析:
IntegrityError
→ 唯一、非空、外键约束DataError
→ 类型或长度不匹配ValidationError
→ 模型字段验证
- 统一返回字典:
{error, model, fields, operation}
2️⃣ 完整示例
import re
from functools import wraps
from django.db import IntegrityError, DataError
from django.core.exceptions import ValidationError, ObjectDoesNotExist, PermissionDenied
from django.apps import apps
# 数据库表名 -> Django 模型映射
TABLE_MODEL_MAP = {m._meta.db_table: m.__name__ for m in apps.get_models()}
# 异常中文映射
ERROR_TRANSLATIONS = {
"ValueError": "值错误:输入值不合法",
"TypeError": "类型错误:操作或函数的类型不匹配",
"KeyError": "键错误:字典中不存在指定键",
"PermissionDenied": "权限不足:您无权执行此操作",
"ValidationError": "数据验证错误",
"DoesNotExist": "数据不存在",
"IntegrityError": "数据库完整性错误",
"DataError": "数据库字段数据错误",
}
# IntegrityError 常见模式
INTEGRITY_PATTERNS = {
r"FOREIGN KEY constraint failed": "外键约束失败",
r"NOT NULL constraint failed: (.+?)\.(.+)": "非空约束失败: 表 {table}, 字段 {field}",
r"UNIQUE constraint failed: (.+?)\.(.+)": "唯一约束失败: 表 {table}, 字段 {field}",
}
def parse_integrity_error(exc):
msg = str(exc)
for pattern, desc in INTEGRITY_PATTERNS.items():
m = re.search(pattern, msg)
if m:
if "{table}" in desc:
table, field = m.groups()
model_name = TABLE_MODEL_MAP.get(table, table)
return desc.format(table=model_name, field=field), model_name, [field]
return desc, None, None
return msg, None, None
def parse_foreign_key_error(exc):
msg = str(exc)
m = re.search(r'violates foreign key constraint "(.+?)" on table "(.+?)"', msg)
if m:
_, referencing_table = m.groups()
return TABLE_MODEL_MAP.get(referencing_table, referencing_table)
if "FOREIGN KEY constraint failed" in msg:
return "未知引用模型"
return None
def orm_auto(func):
"""
高阶装饰器:自动捕获 ORM 异常并返回中文提示
"""
@wraps(func)
def wrapper(*args, **kwargs):
# 尝试自动获取操作模型和字段
instance = kwargs.get("instance", None)
model_name = None
fields = None
operation = None
if instance:
model_name = instance._meta.model_name.capitalize()
fields = list(kwargs.get("data", {}).keys()) if kwargs.get("data") else None
# 如果第一个参数是 Model 实例
elif args and hasattr(args[0], "_meta"):
instance = args[0]
model_name = instance._meta.model_name.capitalize()
fields = list(kwargs.get("data", {}).keys()) if kwargs.get("data") else None
# 判断操作类型
if func.__name__ in ["create", "save"]:
operation = "create/update"
elif func.__name__ in ["delete"]:
operation = "delete"
try:
return func(*args, **kwargs)
except Exception as exc:
error_type = type(exc).__name__
message = ERROR_TRANSLATIONS.get(error_type, str(exc))
# IntegrityError
if isinstance(exc, IntegrityError):
fk_model = parse_foreign_key_error(exc)
if fk_model and operation == "delete":
message = f"操作失败:存在外键依赖,模型 '{fk_model}' 中有数据引用该对象,请先删除或解除依赖。"
model_name = fk_model
else:
msg, m_name, flds = parse_integrity_error(exc)
message = msg
if m_name:
model_name = m_name
if flds:
fields = flds
# DataError
elif isinstance(exc, DataError):
message = f"数据错误:{exc}"
# ValidationError
elif isinstance(exc, ValidationError):
message = f"数据验证错误: {exc}"
# ObjectDoesNotExist
elif isinstance(exc, ObjectDoesNotExist):
if model_name:
message = f"{model_name} 数据不存在"
else:
message = "数据不存在"
# PermissionDenied
elif isinstance(exc, PermissionDenied):
message = "权限不足:您无权执行此操作"
return {
"error": message,
"model": model_name,
"fields": fields,
"operation": operation
}
return wrapper
3️⃣ 使用示例
from myapp.models import User, Profile
@orm_auto
def create_user(data):
return User.objects.create(**data)
@orm_auto
def update_user(instance, data):
for k, v in data.items():
setattr(instance, k, v)
instance.save()
@orm_auto
def delete_user(instance):
instance.delete()
4️⃣ 调用示例
result = create_user({"email": "test@example.com", "name": "Test"})
if result and "error" in result:
print(result)
输出示例(唯一约束错误):
{
"error": "唯一约束失败: 表 User, 字段 email",
"model": "User",
"fields": ["email"],
"operation": "create/update"
}
删除时外键约束错误:
{
"error": "操作失败:存在外键依赖,模型 'Profile' 中有数据引用该对象,请先删除或解除依赖。",
"model": "Profile",
"fields": null,
"operation": "delete"
}
✅ 特点:
- 零手动装饰器(只需要装饰 ORM 函数即可)
- 自动解析创建/更新/删除约束错误
- 外键、唯一、非空、数据类型长度、验证错误均支持
- 返回统一字典,前端友好显示
明白,我给你设计一个 完全自动化的 ORM 异常捕获方案,特点:
- 无需装饰器,直接对任意 Django Model 的
create
/save
/delete
/update
自动捕获异常 - 支持 增删改查
- 返回统一 字典格式:
error(中文提示)
、model(模型名)
、fields(字段)
、operation(操作类型)
- 自动解析 外键约束、唯一约束、非空约束、数据类型约束、验证错误
1️⃣ 自动化 ORM 捕获工具
import re
from django.db import models, IntegrityError, DataError
from django.core.exceptions import ValidationError, ObjectDoesNotExist, PermissionDenied
from django.apps import apps
# 数据库表名 -> Django 模型映射
TABLE_MODEL_MAP = {m._meta.db_table: m.__name__ for m in apps.get_models()}
# 异常中文映射
ERROR_TRANSLATIONS = {
"ValueError": "值错误:输入值不合法",
"TypeError": "类型错误:操作或函数的类型不匹配",
"KeyError": "键错误:字典中不存在指定键",
"PermissionDenied": "权限不足:您无权执行此操作",
"ValidationError": "数据验证错误",
"DoesNotExist": "数据不存在",
"IntegrityError": "数据库完整性错误",
"DataError": "数据库字段数据错误",
}
# 常见 IntegrityError 模式
INTEGRITY_PATTERNS = {
r"FOREIGN KEY constraint failed": "外键约束失败",
r"NOT NULL constraint failed: (.+?)\.(.+)": "非空约束失败: 表 {table}, 字段 {field}",
r"UNIQUE constraint failed: (.+?)\.(.+)": "唯一约束失败: 表 {table}, 字段 {field}",
}
def parse_integrity_error(exc):
msg = str(exc)
for pattern, desc in INTEGRITY_PATTERNS.items():
m = re.search(pattern, msg)
if m:
if "{table}" in desc:
table, field = m.groups()
model_name = TABLE_MODEL_MAP.get(table, table)
return desc.format(table=model_name, field=field), model_name, [field]
return desc, None, None
return msg, None, None
def parse_foreign_key_error(exc):
msg = str(exc)
# PostgreSQL 风格
m = re.search(r'violates foreign key constraint "(.+?)" on table "(.+?)"', msg)
if m:
_, referencing_table = m.groups()
return TABLE_MODEL_MAP.get(referencing_table, referencing_table)
# SQLite/MySQL 风格
if "FOREIGN KEY constraint failed" in msg:
return "未知引用模型"
return None
# 自动异常处理函数
def handle_orm_operation(func, *args, **kwargs):
"""
func: 实际 ORM 操作函数,如 obj.save, Model.objects.create, obj.delete
自动捕获异常并返回统一字典
"""
model_name = None
fields = None
operation = None
# 尝试获取操作对象和字段
instance = kwargs.get("instance", None)
data = kwargs.get("data", None)
if instance and hasattr(instance, "_meta"):
model_name = instance._meta.model_name.capitalize()
fields = list(data.keys()) if data else None
elif args and hasattr(args[0], "_meta"):
instance = args[0]
model_name = instance._meta.model_name.capitalize()
fields = list(data.keys()) if data else None
# 判断操作类型
op_name = func.__name__.lower()
if op_name in ["create", "save"]:
operation = "create/update"
elif op_name in ["delete"]:
operation = "delete"
else:
operation = op_name
try:
return func(*args, **kwargs)
except Exception as exc:
message = ERROR_TRANSLATIONS.get(type(exc).__name__, str(exc))
if isinstance(exc, IntegrityError):
fk_model = parse_foreign_key_error(exc)
if fk_model and operation == "delete":
message = f"操作失败:存在外键依赖,模型 '{fk_model}' 中有数据引用该对象,请先删除或解除依赖。"
model_name = fk_model
else:
msg, m_name, flds = parse_integrity_error(exc)
message = msg
if m_name:
model_name = m_name
if flds:
fields = flds
elif isinstance(exc, DataError):
message = f"数据错误:{exc}"
elif isinstance(exc, ValidationError):
message = f"数据验证错误: {exc}"
elif isinstance(exc, ObjectDoesNotExist):
message = f"{model_name or '数据'}不存在"
elif isinstance(exc, PermissionDenied):
message = "权限不足:您无权执行此操作"
return {
"error": message,
"model": model_name,
"fields": fields,
"operation": operation
}
2️⃣ 使用方法
from myapp.models import User, Profile
# 创建
result = handle_orm_operation(User.objects.create, email="test@example.com", name="Test")
if "error" in result:
print(result)
# 更新
user = User.objects.get(id=1)
result = handle_orm_operation(user.save)
if "error" in result:
print(result)
# 删除
profile = Profile.objects.get(id=10)
result = handle_orm_operation(profile.delete)
if "error" in result:
print(result)
3️⃣ 增删改查出错示例返回信息
操作 | 错误类型 | 返回信息 |
---|---|---|
创建 | 唯一约束失败 | {"error": "唯一约束失败: 表 User, 字段 email", "model": "User", "fields": ["email"], "operation": "create/update"} |
创建/更新 | 非空约束失败 | {"error": "非空约束失败: 表 User, 字段 name", "model": "User", "fields": ["name"], "operation": "create/update"} |
创建/更新 | 数据类型或长度错误 | {"error": "数据错误:value too long for type varchar", "model": "User", "fields": ["email"], "operation": "create/update"} |
删除 | 外键约束失败 | {"error": "操作失败:存在外键依赖,模型 'Profile' 中有数据引用该对象,请先删除或解除依赖。", "model": "Profile", "fields": null, "operation": "delete"} |
查询/更新 | 对象不存在 | {"error": "User 数据不存在", "model": "User", "fields": null, "operation": "update"} |
权限不足 | PermissionDenied | {"error": "权限不足:您无权执行此操作", "model": null, "fields": null, "operation": null} |
验证错误 | ValidationError | {"error": "数据验证错误: [具体验证错误信息]", "model": "User", "fields": ["email"], "operation": "create/update"} |
这个方案 完全零改动:
- 你只需要调用
handle_orm_operation
包裹你的 ORM 方法 - 所有增删改查异常都会自动返回 中文提示 + 模型 + 字段 + 操作类型
- 支持 外键约束、唯一约束、非空约束、数据类型错误、验证错误、权限错误
完全可以,而且这是很多团队遇到的问题:Django/数据库返回的报错信息里,很多都是英文原始信息,直接返回前端不够友好。我们可以做 二次解析,把常见的创建/更新错误完全转换成中文,并指出 字段、约束类型、操作建议。
下面我帮你优化 handle_orm_operation
,针对 创建/更新操作,对常见错误(唯一约束、非空、类型、长度、验证错误)做更清晰的中文提示:
1️⃣ 优化后的异常解析函数
def parse_create_update_error(exc, model_name=None, data_fields=None):
"""
专门解析创建/更新时的异常,返回中文提示 + 受影响字段
"""
msg = str(exc)
fields = data_fields
# IntegrityError
if isinstance(exc, IntegrityError):
# 外键约束(一般更新时触发)
fk_model = parse_foreign_key_error(exc)
if fk_model:
return f"操作失败:存在外键依赖,模型 '{fk_model}' 中有数据引用该对象,请先删除或解除依赖。", [fk_model]
# 唯一约束
m = re.search(r"UNIQUE constraint failed: (.+?)\.(.+)", msg)
if m:
table, field = m.groups()
model = TABLE_MODEL_MAP.get(table, table)
return f"唯一约束失败:模型 '{model}' 的字段 '{field}' 已存在相同值,请修改后重试。", [field]
# 非空约束
m = re.search(r"NOT NULL constraint failed: (.+?)\.(.+)", msg)
if m:
table, field = m.groups()
model = TABLE_MODEL_MAP.get(table, table)
return f"非空约束失败:模型 '{model}' 的字段 '{field}' 不能为空,请填写值后重试。", [field]
# 默认
return f"数据库完整性错误: {msg}", fields
# DataError 数据类型/长度错误
elif isinstance(exc, DataError):
return f"数据类型或长度错误: {msg}", fields
# ValidationError 模型验证
elif isinstance(exc, ValidationError):
error_list = []
if hasattr(exc, "message_dict"):
for k, v in exc.message_dict.items():
error_list.append(f"字段 '{k}': {','.join(v)}")
return f"数据验证错误: {'; '.join(error_list)}", list(exc.message_dict.keys())
return f"数据验证错误: {msg}", fields
# 默认
return f"操作失败: {msg}", fields
2️⃣ 在 handle_orm_operation
中调用
def handle_orm_operation(func, *args, **kwargs):
model_name = None
fields = None
operation = None
# 尝试获取对象和字段
instance = kwargs.get("instance", None)
data = kwargs.get("data", None)
if instance and hasattr(instance, "_meta"):
model_name = instance._meta.model_name.capitalize()
fields = list(data.keys()) if data else None
elif args and hasattr(args[0], "_meta"):
instance = args[0]
model_name = instance._meta.model_name.capitalize()
fields = list(data.keys()) if data else None
op_name = func.__name__.lower()
if op_name in ["create", "save"]:
operation = "create/update"
elif op_name in ["delete"]:
operation = "delete"
else:
operation = op_name
try:
return func(*args, **kwargs)
except Exception as exc:
if operation == "create/update":
message, affected_fields = parse_create_update_error(exc, model_name, fields)
elif operation == "delete":
fk_model = parse_foreign_key_error(exc)
if fk_model:
message = f"删除失败:存在外键依赖,模型 '{fk_model}' 中有数据引用该对象,请先删除或解除依赖。"
affected_fields = None
model_name = fk_model
else:
message, affected_fields = parse_integrity_error(exc)
else:
message = str(exc)
affected_fields = fields
return {
"error": message,
"model": model_name,
"fields": affected_fields,
"operation": operation
}
3️⃣ 示例输出(更中文化)
唯一约束错误
{
"error": "唯一约束失败:模型 'User' 的字段 'email' 已存在相同值,请修改后重试。",
"model": "User",
"fields": ["email"],
"operation": "create/update"
}
非空约束错误
{
"error": "非空约束失败:模型 'User' 的字段 'name' 不能为空,请填写值后重试。",
"model": "User",
"fields": ["name"],
"operation": "create/update"
}
验证错误
{
"error": "数据验证错误: 字段 'email': 请输入有效邮箱地址",
"model": "User",
"fields": ["email"],
"operation": "create/update"
}
数据类型或长度错误
{
"error": "数据类型或长度错误: value too long for type varchar",
"model": "User",
"fields": ["email"],
"operation": "create/update"
}
✅ 这样就完全解决了 英文原始信息太晦涩 的问题:
- 自动把 唯一约束、非空约束、外键约束、验证错误、数据类型错误 转成清晰中文
- 告诉用户 哪个模型、哪个字段 出错
- 给出 操作建议,比如修改字段、填写值或先删除依赖数据
以下是一个通用的 Python 函数,用于将英文异常信息解析并翻译为中文,并可根据 模型英文名到中文名的映射 动态替换:
函数代码
def translate_exception_message(error_message: str, model_name_map: dict = None) -> str:
"""
将英文异常信息解析并翻译成中文,同时可根据模型名映射替换为中文名。
:param error_message: str, 原始英文错误信息
:param model_name_map: dict, 模型英文名到中文名的映射,如 {"Dataset": "数据集"}
:return: str, 翻译后的中文错误信息
"""
if model_name_map is None:
model_name_map = {}
# 基本翻译映射,可持续扩充
translation_map = {
"does not exist": "不存在",
"already exists": "已存在",
"cannot be null": "不能为空",
"not a valid": "不是有效的",
"invalid": "无效的",
"unique constraint": "唯一约束",
"foreign key constraint": "外键约束",
"value too long": "值过长",
"duplicate key": "重复的键值",
"permission denied": "没有权限",
"connection refused": "无法连接数据库",
"timeout": "超时",
}
translated = error_message
# 1. 替换模型名
for eng_name, cn_name in model_name_map.items():
if eng_name in translated:
translated = translated.replace(eng_name, cn_name)
# 2. 替换常见错误关键词
for eng, cn in translation_map.items():
if eng in translated.lower():
translated = translated.replace(eng, cn).replace(eng.capitalize(), cn)
# 3. 增强处理:去掉多余技术栈信息 (可按需扩展)
translated = translated.replace("django.db.utils.", "").replace("IntegrityError: ", "")
translated = translated.replace("psycopg2.errors.", "").replace("Detail: ", "详情:")
return translated
使用示例
error_msg = "django.db.utils.IntegrityError: duplicate key value violates unique constraint \"dataset_name_key\""
model_map = {"Dataset": "数据集"}
print(translate_exception_message(error_msg, model_map))
# 输出: "重复的键值 value violates 唯一约束 \"数据集_name_key\""
说明
- 支持模型名替换:
model_name_map
用于将英文模型名替换成中文。 - 可扩展性:
translation_map
可逐步补充更多数据库和 Django 常见错误。 - 适配所有异常:直接传入
str(exception)
即可。
以下是一个通用异常处理函数,既能捕获数据库相关异常,又支持将错误信息中涉及的模型名、字段名替换为中文描述。
1. 定义模型中文映射
# 可在单独的 config 文件中维护
MODEL_NAME_MAP = {
"User": "用户",
"Dataset": "数据集",
"DatasetVersion": "数据集版本",
}
FIELD_NAME_MAP = {
"username": "用户名",
"email": "邮箱",
"version": "版本号",
}
2. 通用异常处理函数
from django.db import DatabaseError, IntegrityError, OperationalError
import re
import logging
logger = logging.getLogger(__name__)
def replace_with_chinese(message: str) -> str:
# 替换模型名
for eng, cn in MODEL_NAME_MAP.items():
message = re.sub(rf"\b{eng}\b", cn, message)
# 替换字段名
for eng, cn in FIELD_NAME_MAP.items():
message = re.sub(rf"\b{eng}\b", cn, message)
return message
def handle_db_exception(func):
"""
装饰器:捕获数据库异常并返回更友好的中文错误信息
"""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except (IntegrityError, OperationalError, DatabaseError) as e:
raw_msg = str(e)
user_msg = replace_with_chinese(raw_msg)
logger.error(f"数据库操作异常: {raw_msg}")
return {"success": False, "error": user_msg}
except Exception as e:
logger.error(f"未知异常: {str(e)}")
return {"success": False, "error": "系统发生未知错误,请联系管理员"}
return wrapper
3. 使用示例
@handle_db_exception
def create_dataset():
# 示例数据库操作
from myapp.models import Dataset
Dataset.objects.create(name="test", version=1)
return {"success": True}
当数据库报错时(例如唯一性约束失败),英文信息会被替换为中文描述并返回。
以下是一个可拷贝、可直接运行的方案,分为两部分:
1. 基于 verbose_name
自动生成映射
在 utils/error_maps.py
中:
# utils/error_maps.py
from django.apps import apps
def generate_name_maps():
"""
动态生成模型和字段的中文名映射
"""
model_name_map = {}
field_name_map = {}
for model in apps.get_models():
# 获取模型的 Meta 信息
model_meta = model._meta
model_name_map[model.__name__] = model_meta.verbose_name
field_map = {}
for field in model_meta.fields:
field_map[field.name] = field.verbose_name
field_name_map[model.__name__] = field_map
return model_name_map, field_name_map
# 在导入时即生成一次映射
MODEL_NAME_MAP, FIELD_NAME_MAP = generate_name_maps()
2. 错误信息中文化函数
在 utils/error_translator.py
中:
# utils/error_translator.py
import re
from .error_maps import MODEL_NAME_MAP, FIELD_NAME_MAP
def translate_error_message(message: str) -> str:
"""
将错误信息中的模型名和字段名翻译为中文
"""
translated_message = message
# 替换模型名
for eng_name, cn_name in MODEL_NAME_MAP.items():
translated_message = re.sub(rf'\b{eng_name}\b', cn_name, translated_message)
# 替换字段名
for model_eng, fields_map in FIELD_NAME_MAP.items():
for field_eng, field_cn in fields_map.items():
translated_message = re.sub(rf'\b{field_eng}\b', field_cn, translated_message)
return translated_message
3. 使用示例
from utils.error_translator import translate_error_message
try:
# 模拟一个错误
raise ValueError("Invalid value for field user_name in model UserProfile")
except ValueError as e:
print(translate_error_message(str(e)))
如果模型 UserProfile
的 verbose_name
是 用户资料
,字段 user_name
的 verbose_name
是 用户名
,则输出:
Invalid value for field 用户名 in model 用户资料
4. 注意点
- 需要在 Django 启动后(
apps.ready
)使用,否则apps.get_models()
可能获取不到模型。 - 如果使用 多语言,需要确保
verbose_name
使用了gettext_lazy
。 - 可以在 中间件 或 异常捕获器 中统一调用
translate_error_message
。
下面给出一个完整的、可直接使用的方案,支持以下功能:
- 自动从 Django 模型的
verbose_name
和字段的verbose_name
生成映射表; - 将 Django 的原始英文错误信息 转换为中文提示;
- 显示为“模型中文名 + 字段中文名 + 错误信息中文描述”的格式。
1. 自动生成映射表
在 utils/model_map.py
中创建以下代码:
from django.apps import apps
def generate_model_field_maps():
"""
自动生成模型名称和字段名称映射
MODEL_NAME_MAP: { 'App.ModelName': '模型中文名' }
FIELD_NAME_MAP: { 'App.ModelName.field_name': '字段中文名' }
"""
MODEL_NAME_MAP = {}
FIELD_NAME_MAP = {}
for model in apps.get_models():
app_label = model._meta.app_label
model_name = model.__name__
verbose_model_name = model._meta.verbose_name
model_key = f"{app_label}.{model_name}"
MODEL_NAME_MAP[model_key] = str(verbose_model_name)
for field in model._meta.fields:
field_key = f"{model_key}.{field.name}"
FIELD_NAME_MAP[field_key] = str(field.verbose_name)
return MODEL_NAME_MAP, FIELD_NAME_MAP
# 在应用启动时生成一次,避免每次都扫描
MODEL_NAME_MAP, FIELD_NAME_MAP = generate_model_field_maps()
2. 错误信息中文化处理
在 utils/error_translator.py
中:
import re
from .model_map import MODEL_NAME_MAP, FIELD_NAME_MAP
# 常见错误的中文化模板
ERROR_TRANSLATION = [
(re.compile(r"This field cannot be blank\."), "此字段不能为空"),
(re.compile(r"This field is required\."), "此字段是必填项"),
(re.compile(r"Ensure this value has at least (\d+) characters? \(it has (\d+)\)\."),
"确保此值至少有 {0} 个字符(当前为 {1} 个)"),
(re.compile(r"Ensure this value has at most (\d+) characters? \(it has (\d+)\)\."),
"确保此值最多有 {0} 个字符(当前为 {1} 个)"),
(re.compile(r"Enter a valid email address\."), "请输入有效的电子邮件地址"),
# 可按需扩展更多匹配规则
]
def translate_error_message(message):
"""将英文错误信息翻译为中文"""
for pattern, translation in ERROR_TRANSLATION:
match = pattern.match(message)
if match:
return translation.format(*match.groups())
return message # 无匹配时返回原始信息
def format_error(model, field, error_message):
"""
组合中文化的完整错误提示:
- 通过 model.__class__ 获取模型信息
- 通过 field 获取字段中文名
"""
app_label = model._meta.app_label
model_name = model.__class__.__name__
model_key = f"{app_label}.{model_name}"
field_key = f"{model_key}.{field}"
model_cn = MODEL_NAME_MAP.get(model_key, model_name)
field_cn = FIELD_NAME_MAP.get(field_key, field)
error_cn = translate_error_message(error_message)
return f"{model_cn} 的 {field_cn}:{error_cn}"
3. 使用示例
from django.core.exceptions import ValidationError
from utils.error_translator import format_error
def create_user(data):
from myapp.models import User
user = User(**data)
try:
user.full_clean()
user.save()
except ValidationError as e:
# e.message_dict 形如 {'username': ['This field cannot be blank.']}
for field, errors in e.message_dict.items():
for err in errors:
print(format_error(user, field, err))
输出示例:
用户 的 用户名:此字段不能为空
4. 优化说明
- 一次生成映射:
generate_model_field_maps()
在 Django 启动时执行,避免重复扫描; - 可扩展错误模板:
ERROR_TRANSLATION
可根据需求逐步完善; - 可直接拷贝使用:分为两个文件
model_map.py
和error_translator.py
,无需修改其他部分。
可以基于 错误码分类 + 中文提示模板库 + 正则动态匹配 的方式构建一个通用异常处理器,能够自动识别不同数据库和第三方库的异常,并输出对应的中文错误信息。以下是一个可扩展方案:
1. 错误码与提示模板设计
# error_messages.py
ERROR_MESSAGES = {
"IntegrityError": {
r"unique constraint.*violated": {
"code": "DB1001",
"message": "唯一约束冲突:字段或组合值已存在,无法重复。"
},
r"null value in column .* violates not-null constraint": {
"code": "DB1002",
"message": "字段不能为空,请检查必填项。"
},
r"foreign key constraint .*": {
"code": "DB1003",
"message": "外键约束错误:关联对象不存在或已删除。"
}
},
"OperationalError": {
r"could not connect to server": {
"code": "DB2001",
"message": "无法连接数据库,请检查服务是否启动。"
},
r"timeout expired": {
"code": "DB2002",
"message": "数据库连接超时,请重试或检查网络。"
}
},
"ProgrammingError": {
r"column .* does not exist": {
"code": "DB3001",
"message": "SQL 错误:引用了不存在的字段。"
},
r"syntax error at or near": {
"code": "DB3002",
"message": "SQL 语法错误,请检查语句。"
}
}
}
2. 动态异常解析器
import re
from psycopg2 import IntegrityError, OperationalError, ProgrammingError
from error_messages import ERROR_MESSAGES
class DBErrorTranslator:
def __init__(self):
self.error_map = ERROR_MESSAGES
def translate(self, exc: Exception):
exc_type = type(exc).__name__
exc_msg = str(exc)
# 动态匹配错误码
if exc_type in self.error_map:
for pattern, info in self.error_map[exc_type].items():
if re.search(pattern, exc_msg, re.IGNORECASE):
return {
"error_code": info["code"],
"error_message": info["message"],
"raw_message": exc_msg # 可选保留
}
# 未匹配到的情况
return {
"error_code": "DB9999",
"error_message": "未知数据库错误,请联系管理员。",
"raw_message": exc_msg
}
3. 使用示例(Django 或任意数据库操作场景)
translator = DBErrorTranslator()
try:
# 执行数据库操作
obj.save()
except (IntegrityError, OperationalError, ProgrammingError) as e:
error_info = translator.translate(e)
print(error_info) # 生产环境可返回给前端 JSON
4. 支持第三方库模型的扫描
如果需要根据模型动态提示:
-
结合 Django 的 apps.get_models() 或 SQLAlchemy 的 Base.metadata.tables 自动扫描模型字段及元信息。
-
结合异常类型及模型字段生成更具体提示,例如:
唯一约束冲突:字段 [username] 的值已存在。
可通过在 translate
中增加一个模型解析器,提取出错误信息中的字段名,再匹配模型定义,生成提示。
下面是一个可扩展的 数据库异常处理模板库,支持:
- 错误码分类(唯一约束、外键约束、检查约束、字段类型错误等)。
- 中文提示模板,可自动替换字段的
verbose_name
(或help_text
)。 - 动态正则匹配,兼容不同数据库(PostgreSQL、MySQL、SQLite)。
- 扫描 Django 项目中所有模型(含第三方库),以获取字段中文别名。
1. 异常处理核心逻辑
# db_error_handler.py
import re
from django.apps import apps
from django.db import IntegrityError, DataError
from psycopg2.errors import UniqueViolation, ForeignKeyViolation, CheckViolation
class DBErrorTranslator:
def __init__(self):
self.field_alias_map = self._build_field_alias_map()
self.error_patterns = self._load_error_patterns()
def _build_field_alias_map(self):
"""
扫描所有模型,构建 {table_name: {column_name: verbose_name}} 映射
"""
field_map = {}
for model in apps.get_models():
table = model._meta.db_table
field_map[table] = {}
for field in model._meta.get_fields():
if hasattr(field, 'verbose_name'):
field_map[table][field.column] = field.verbose_name
return field_map
def _load_error_patterns(self):
"""
定义多数据库兼容的错误匹配模板,支持动态字段替换
"""
return {
"unique_violation": {
"regex": [
re.compile(r'duplicate key value violates unique constraint "(?P<constraint>.+)"'),
re.compile(r'UNIQUE constraint failed: (?P<table>[\w_]+)\.(?P<field>[\w_]+)')
],
"message": "字段 {field_name} 的值已存在,不能重复。"
},
"foreign_key_violation": {
"regex": [
re.compile(r'violates foreign key constraint "(?P<constraint>.+)"')
],
"message": "字段 {field_name} 引用的数据不存在。"
},
"check_violation": {
"regex": [
re.compile(r'new row for relation ".+" violates check constraint')
],
"message": "字段 {field_name} 的值不符合约束条件。"
},
"data_error": {
"regex": [
re.compile(r'value too long for type character varying\((?P<max_length>\d+)\)')
],
"message": "字段 {field_name} 的值超出最大长度 {max_length}。"
}
}
def translate(self, exc: Exception) -> str:
"""
将数据库异常翻译为中文提示
"""
error_message = str(exc)
for category, cfg in self.error_patterns.items():
for pattern in cfg["regex"]:
match = pattern.search(error_message)
if match:
info = match.groupdict()
field_name = self._resolve_field_name(info)
return cfg["message"].format(field_name=field_name, **info)
return "发生数据库错误,请检查输入数据。"
def _resolve_field_name(self, info: dict):
"""
根据表名、字段名查找 verbose_name
"""
table = info.get("table")
field = info.get("field")
if table and field:
alias = self.field_alias_map.get(table, {}).get(field)
return alias or field
return "未知字段"
2. 在 Django 中使用
# views.py
from django.db import IntegrityError, DataError
from django.http import JsonResponse
from .db_error_handler import DBErrorTranslator
translator = DBErrorTranslator()
def create_object(request):
try:
# 保存逻辑
obj.save()
return JsonResponse({"success": True})
except (IntegrityError, DataError) as e:
msg = translator.translate(e)
return JsonResponse({"success": False, "error": msg})
3. 支持第三方库模型
apps.get_models()
会扫描 所有已注册的 Django 应用模型,包括第三方库(只要它们在 INSTALLED_APPS
中)。
4. 优点
- 错误码分类清晰,可扩展不同数据库。
- 自动中文化字段名,提升用户体验。
- 正则匹配动态提取字段信息,减少硬编码。
以下是一个扩展后的 PostgreSQL 异常类与错误码对照表(50+条),结合 Django + psycopg2 场景,支持分类、详细注释,并提供如何在异常处理中提取表和字段信息并映射到 verbose_name
。
1. 错误码分类表(50+ 条)
完整表格(分类 + 注释)
分类 | 错误码 | 异常类 | 说明 |
---|---|---|---|
唯一约束与主键冲突 | |||
23505 | IntegrityError | 唯一约束冲突 (Unique Violation) – 插入或更新数据时违反唯一性约束 | |
23502 | IntegrityError | 非空约束冲突 (Not Null Violation) – 向 NOT NULL 字段插入 NULL |
|
23P01 | IntegrityError | 排序约束冲突 (Exclusion Violation) – 违反排除约束(如空间数据唯一性) | |
外键与引用完整性错误 | |||
23503 | IntegrityError | 外键约束冲突 (Foreign Key Violation) – 试图插入不存在的外键值或删除被引用的数据 | |
23514 | IntegrityError | 检查约束冲突 (Check Violation) – 数据不满足 CHECK 约束条件 | |
数据类型与转换错误 | |||
22P02 | DataError | 无效文本表示 (Invalid Text Representation) – 类型转换失败,例如字符串转整数失败 | |
22007 | DataError | 无效日期时间格式 (Invalid Datetime Format) | |
22008 | DataError | 日期时间超出范围 (Datetime Field Overflow) | |
22003 | DataError | 数值超出范围 (Numeric Value Out of Range) | |
22018 | DataError | 无效字符值 (Invalid Character Value for Cast) | |
算术与数值运算错误 | |||
22012 | DataError | 除以零 (Division by Zero) | |
2200E | DataError | 数值精度丢失 (Invalid Escape Character) | |
语法与查询相关错误 | |||
42601 | ProgrammingError | SQL 语法错误 | |
42501 | PermissionError | 权限不足 (Insufficient Privilege) | |
42P01 | ProgrammingError | 表不存在 (Undefined Table) | |
42703 | ProgrammingError | 列不存在 (Undefined Column) | |
42602 | ProgrammingError | 无效名称 (Invalid Name) | |
事务与锁错误 | |||
40001 | OperationalError | 序列化失败 (Serialization Failure) – 高并发下事务冲突 | |
40P01 | OperationalError | 死锁检测 (Deadlock Detected) | |
55P03 | OperationalError | 无法获取锁 (Lock Not Available) | |
对象定义错误 | |||
42701 | ProgrammingError | 重复列 (Duplicate Column) | |
42P07 | ProgrammingError | 重复表 (Duplicate Table) | |
42P16 | ProgrammingError | 无效表定义 (Invalid Table Definition) | |
字符串与编码错误 | |||
22021 | DataError | 无效字符 (Character Not In Repertoire) | |
22023 | DataError | 无效参数值 (Invalid Parameter Value) | |
2201X | DataError | 字符串长度超出限制 (String Data Right Truncation) | |
触发器与函数错误 | |||
2F005 | InternalError | 触发器协议违规 (Trigger Protocol Violated) | |
2F002 | InternalError | 触发器函数返回不正确 (Trigger Function Returned Invalid Value) | |
系统与资源错误 | |||
53100 | OperationalError | 磁盘空间不足 (Disk Full) | |
53200 | OperationalError | 内存不足 (Out of Memory) | |
53300 | OperationalError | 过多连接 (Too Many Connections) | |
53400 | OperationalError | 配置错误 (Configuration Limit Exceeded) | |
其他约束与数据完整性错误 | |||
2201E | DataError | 数组下标超出范围 (Array Subscript Error) | |
2200G | DataError | 最小值大于最大值 (Invalid Interval) | |
22019 | DataError | 无效转义字符 (Invalid Escape Character) | |
2200L | DataError | 不允许不确定 (Not Allowed Null) | |
动态 SQL 错误 | |||
07003 | ProgrammingError | 参数数量不匹配 (Parameter Number Mismatch) | |
07006 | ProgrammingError | 无效参数类型 (Invalid Parameter Type) | |
认证与安全错误 | |||
28P01 | OperationalError | 用户认证失败 (Invalid Password) | |
28000 | OperationalError | 用户账户未授权 (Invalid Authorization Specification) | |
I/O 错误 | |||
58030 | OperationalError | 文件读取错误 (I/O Error) | |
58P01 | OperationalError | 文件不存在 (Undefined File) | |
58P02 | OperationalError | 无法访问文件 (Cannot Access File) |
2. 异常处理与表/字段 verbose_name 映射
捕获异常并提取表和字段
from django.db import IntegrityError, DataError, ProgrammingError
from psycopg2 import errorcodes
from psycopg2.errors import UniqueViolation, ForeignKeyViolation, NotNullViolation
def handle_db_exception(exc):
if isinstance(exc, IntegrityError):
sqlstate = exc.__cause__.pgcode # PostgreSQL 错误码
message = exc.__cause__.diag.message_detail # 详细信息
table = exc.__cause__.diag.table_name
column = exc.__cause__.diag.column_name
# 通过 Django Model 元数据获取 verbose_name
verbose_table = verbose_column = None
try:
from django.apps import apps
for model in apps.get_models():
if model._meta.db_table == table:
verbose_table = model._meta.verbose_name
for f in model._meta.fields:
if f.column == column:
verbose_column = f.verbose_name
break
break
except Exception:
pass
return {
"error_code": sqlstate,
"table": verbose_table or table,
"column": verbose_column or column,
"message": message,
}
else:
return {"error": str(exc)}
异常处理示例
try:
obj.save()
except IntegrityError as e:
info = handle_db_exception(e)
print(f"错误码: {info['error_code']}, 表: {info['table']}, 字段: {info['column']}, 信息: {info['message']}")
3. 建议的维护方案
- 将错误码表放入单独的
error_codes.py
文件。 - 提供
get_error_description(pgcode)
方法,快速返回错误类型与建议处理方式。 - 定期更新 PostgreSQL 官方文档中的新错误码。
以下是 PostgreSQL 常见异常类(基于 Django
和 psycopg2
驱动)及其 SQLSTATE 错误码 对照表,并说明如何在异常处理中区分它们。
1. Django 异常类(封装了 psycopg2)
Django 在 django.db
下提供了自己的数据库异常层次结构,所有数据库异常均继承自 django.db.Error
。
常见异常类及含义
Django 异常类 | 对应 psycopg2 异常 | 说明 |
---|---|---|
DatabaseError |
psycopg2.DatabaseError |
所有数据库相关错误的基类 |
DataError |
psycopg2.DataError |
数据处理错误,如数值超出范围、字符串长度超限 |
OperationalError |
psycopg2.OperationalError |
连接失败、事务状态异常等 |
IntegrityError |
psycopg2.IntegrityError |
违反完整性约束,如主键、外键、唯一约束 |
InternalError |
psycopg2.InternalError |
数据库内部错误 |
ProgrammingError |
psycopg2.ProgrammingError |
SQL 语法错误、表不存在、列不存在等 |
NotSupportedError |
psycopg2.NotSupportedError |
执行数据库不支持的功能 |
2. PostgreSQL SQLSTATE 错误码分类
PostgreSQL 提供 5 个字符的 SQLSTATE 错误码,可用于更细粒度的异常区分。
示例:psycopg2
的异常对象包含 pgcode
和 pgerror
属性。
常见错误码对照表
SQLSTATE | 错误类别 | 典型场景 |
---|---|---|
23505 |
unique_violation | 唯一约束冲突(如重复插入唯一值) |
23503 |
foreign_key_violation | 外键约束冲突 |
23502 |
not_null_violation | 非空约束被违反 |
23514 |
check_violation | CHECK 约束条件不满足 |
40001 |
serialization_failure | 并发事务序列化失败 |
40003 |
statement_completion_unknown | 事务状态不确定 |
08003 |
connection_does_not_exist | 连接丢失 |
08006 |
connection_failure | 连接失败 |
42P01 |
undefined_table | 访问不存在的表 |
42703 |
undefined_column | 访问不存在的列 |
42601 |
syntax_error | SQL 语法错误 |
3. 异常处理中区分方式
from django.db import DatabaseError, IntegrityError, DataError, OperationalError
try:
# 数据库操作
MyModel.objects.create(name='test', unique_field='duplicate')
except IntegrityError as e:
# 通过 SQLSTATE 错误码区分具体原因
if hasattr(e, 'pgcode'):
if e.pgcode == '23505':
print("唯一约束冲突")
elif e.pgcode == '23503':
print("外键约束冲突")
else:
print("其他完整性错误:", e)
except DataError as e:
print("数据错误:", e.pgerror)
except OperationalError as e:
print("操作错误:", e.pgerror)
except DatabaseError as e:
print("通用数据库错误:", e.pgerror)
4. 在 Django REST Framework (DRF) 中全局异常处理
在 settings.py
中配置:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'path.to.custom_exception_handler'
}
实现 custom_exception_handler
:
from rest_framework.views import exception_handler
from django.db import IntegrityError
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
# 处理数据库异常
if isinstance(exc, IntegrityError):
if hasattr(exc, 'pgcode') and exc.pgcode == '23505':
return Response({'detail': '唯一约束冲突'}, status=400)
return Response({'detail': '数据库完整性错误'}, status=400)
return response