中文错误返回

Posted by Shallow Dreameron August 28, 2025

明白,你是想把 Django 或 Python 报错信息的英文翻译成 中文提示,对吗?我可以帮你做到几个层次:

  1. 手动翻译单条错误:你把报错贴给我,我帮你逐句翻译成中文,并解释含义。
  2. 统一映射字典:把常见的 Django 或 Python 错误类型(ValueError, IntegrityError, DoesNotExist, PermissionDenied 等)建立映射,方便在项目里统一显示中文提示。
  3. 自动捕获与翻译:在 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 数据验证错误 表单或模型字段验证未通过

⚡ 额外建议:

  1. 结合异常捕获和正则匹配,可以针对 IntegrityError 的具体原因返回更友好的中文提示。
  2. 对 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)

✅ 功能说明

  1. 通用异常捕获:捕获 Python 和 Django 常见异常,并映射中文提示。
  2. 数据库错误精确提示
    • 外键约束、唯一约束、非空约束的具体字段和表名。
    • 可扩展正则来匹配更多 Postgres/MySQL 异常。
  3. 模型名称返回:如果异常属于 DoesNotExist 或数据库操作异常,尽量返回出错模型名。
  4. 可返回前端 JSON:方便前端显示中文友好提示。

好,我们可以把中间件升级成 高级版,目标是:

  • 捕获 IntegrityErrorDataErrorOperationalError 等数据库错误
  • 自动解析 出错的 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)

⚡ 功能亮点

  1. 中文提示:几乎覆盖所有常见 Python/Django 异常。
  2. 数据库约束解析:外键、唯一、非空约束会给出表和字段信息。
  3. 模型追踪:通过异常堆栈推测可能出错的模型名(可根据项目进一步精细化)。
  4. 统一返回 JSON:方便前端友好显示。

💡 进一步优化思路

  • IntegrityError事务追踪,获取 Model 对象,例如在每个 ORM 操作前记录 instance._meta.model_name,出错时直接返回。
  • 对 Postgres/MySQL 特定错误码做精确匹配,获取更多约束信息。
  • 批量操作bulk_create, update)也可返回具体行号或对象索引。

明白,我给你设计一个 最终版本的全局异常捕获中间件,可以做到:

  • 捕获 Django/Python 常见异常
  • 中文提示
  • 精确返回出错模型、字段、操作类型
  • 支持批量操作和事务追踪

实现思路:

  1. 在每次 ORM 操作前记录操作上下文:包括模型名、字段、操作类型(插入/更新/删除)
  2. 使用线程局部存储保存当前操作上下文,异常时取出
  3. 全局中间件捕获异常,结合上下文返回精准信息

下面是可直接使用的示例:

# 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)

⚡ 使用方法

  1. 全局注册中间件
MIDDLEWARE = [
    # ... 其他中间件
    "your_project.middleware.advanced_exception_middleware.AdvancedExceptionMiddleware",
]
  1. 在 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)
  1. 效果
  • 如果插入重复 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)

⚡ 使用方法

  1. 注册中间件
MIDDLEWARE = [
    # ... 其他中间件
    "your_project.middleware.final_exception_middleware.FinalExceptionMiddleware",
]
  1. 使用 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()
  1. 效果
  • 删除对象时,如果有外键引用:
{
    "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"
}

✅ 优点:

  1. 不依赖中间件,函数/方法独立可用
  2. 自动解析外键约束,提示阻止删除的模型
  3. 统一中文提示 + 模型/字段/操作信息
  4. 可直接扩展 批量操作、更新操作

明白了,你希望最终版本是 完全自动化、零手动装饰器,能做到:

  • 创建、更新、删除时 所有常见数据库约束错误 都能自动捕获
  • 自动返回中文提示
  • 自动推断 出错模型、字段、操作类型
  • 外键约束导致删除失败时,返回引用模型和字段
  • 唯一约束、非空约束、类型错误、数据长度错误等创建/更新错误也能返回

我帮你设计一个 通用函数 + 高阶装饰器 + ORM 自动追踪 方案:


1️⃣ 核心思路

  1. QuerySet/Model 封装:用装饰器包装 ORM 方法(create、update、delete、save 等)
  2. 自动推断模型名:通过对象 _meta.model_name 或 QuerySet 模型
  3. 字段追踪:获取操作数据的字段
  4. 异常解析
    • IntegrityError → 唯一、非空、外键约束
    • DataError → 类型或长度不匹配
    • ValidationError → 模型字段验证
  5. 统一返回字典{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\""

说明

  1. 支持模型名替换model_name_map 用于将英文模型名替换成中文。
  2. 可扩展性translation_map 可逐步补充更多数据库和 Django 常见错误。
  3. 适配所有异常:直接传入 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)))

如果模型 UserProfileverbose_name用户资料,字段 user_nameverbose_name用户名,则输出:

Invalid value for field 用户名 in model 用户资料

4. 注意点

  1. 需要在 Django 启动后(apps.ready)使用,否则 apps.get_models() 可能获取不到模型。
  2. 如果使用 多语言,需要确保 verbose_name 使用了 gettext_lazy
  3. 可以在 中间件异常捕获器 中统一调用 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.pyerror_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 中增加一个模型解析器,提取出错误信息中的字段名,再匹配模型定义,生成提示。


下面是一个可扩展的 数据库异常处理模板库,支持:

  1. 错误码分类(唯一约束、外键约束、检查约束、字段类型错误等)。
  2. 中文提示模板,可自动替换字段的 verbose_name(或 help_text)。
  3. 动态正则匹配,兼容不同数据库(PostgreSQL、MySQL、SQLite)。
  4. 扫描 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 常见异常类(基于 Djangopsycopg2 驱动)及其 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 的异常对象包含 pgcodepgerror 属性。

常见错误码对照表

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