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

如何在Django模型中处理用户自定义格式表达式?Python方案求助

解析含自定义IF函数的表达式解决方案(Django场景)

针对你需要解析类似'(some var)+15+IF((condition),(passed),(not passed))'这类表达式的需求,以下是三种可行方案,兼顾当前需求和未来扩展自定义函数的灵活性:

方案1:正则替换+安全Eval(快速实现)

核心思路是将自定义IF(cond, true, false)语法转换为Python原生的三元表达式true if cond else false,同时限制Eval的执行环境避免安全风险。

代码实现

from django.db import models
import re

class ExpressionModel(models.Model):
    input_expr = models.TextField(verbose_name="用户输入表达式")
    var_values = models.JSONField(default=dict, verbose_name="变量键值对")

    def evaluate(self):
        # 1. 递归替换自定义IF为Python三元表达式
        processed_expr = self._replace_custom_if(self.input_expr)
        # 2. 替换表达式中的变量占位符为实际变量引用
        processed_expr = re.sub(r'\(([\w\s]+)\)', r'var_values["\1"]', processed_expr)
        
        # 3. 安全执行Eval,仅允许指定内置函数
        allowed_builtins = {k: v for k, v in __builtins__.items() if k in ['int', 'float', 'bool']}
        try:
            return eval(processed_expr, {"__builtins__": allowed_builtins}, {"var_values": self.var_values})
        except Exception as e:
            raise ValueError(f"表达式解析失败: {str(e)}")

    @staticmethod
    def _replace_custom_if(expr_str):
        pattern = r'IF\((.*?),(.*?),(.*?)\)'
        # 递归处理嵌套IF结构
        while re.search(pattern, expr_str):
            expr_str = re.sub(pattern, r'\2 if \1 else \3', expr_str)
        return expr_str

优缺点

  • 优点:代码量少、实现快速,适合简单表达式场景
  • 缺点:正则对复杂嵌套IF的处理可能存在边界问题,扩展复杂自定义函数时灵活性不足

方案2:使用PyParsing构建自定义语法解析器(灵活扩展)

PyParsing是专门的语法解析库,可自定义语法规则,完美支持嵌套结构和未来添加新的自定义函数。

代码实现

from django.db import models
from pyparsing import (
    Word, alphas, nums, Forward, Group, Suppress, 
    oneOf, infixNotation, opAssoc, Literal
)

class ExpressionModel(models.Model):
    input_expr = models.TextField(verbose_name="用户输入表达式")
    var_values = models.JSONField(default=dict, verbose_name="变量键值对")

    def evaluate(self):
        # 定义语法规则
        var = Suppress('(') + Word(alphas + ' ') + Suppress(')')
        num = Word(nums + '.').setParseAction(lambda t: float(t[0]))
        
        # 定义IF函数语法
        if_func = Forward()
        expr = Forward()
        if_func << Literal('IF') + Suppress('(') + Group(expr) + Suppress(',') + Group(expr) + Suppress(',') + Group(expr) + Suppress(')')
        
        # 定义算术运算符优先级
        operand = var | num | if_func
        arith_expr = infixNotation(
            operand,
            [
                ('+', 2, opAssoc.LEFT),
                ('-', 2, opAssoc.LEFT),
                ('*', 2, opAssoc.LEFT),
                ('/', 2, opAssoc.LEFT),
            ]
        )

        # 递归求值解析树
        def _eval_parsed(node):
            if isinstance(node, str):
                return self.var_values.get(node.strip())
            elif isinstance(node, float):
                return node
            elif isinstance(node, list):
                if node[0] == 'IF':
                    cond_val = _eval_parsed(node[1])
                    return _eval_parsed(node[2]) if cond_val else _eval_parsed(node[3])
                # 处理算术运算
                op = node[1]
                left = _eval_parsed(node[0])
                right = _eval_parsed(node[2])
                ops = {'+': lambda a,b: a+b, '-': lambda a,b: a-b, '*': lambda a,b: a*b, '/': lambda a,b: a/b}
                return ops[op](left, right)
            return None

        parsed_tree = arith_expr.parseString(self.input_expr)[0]
        return _eval_parsed(parsed_tree)

优缺点

  • 优点:完全自定义语法规则,支持复杂嵌套和任意自定义函数扩展,无正则的边界问题
  • 缺点:需要学习PyParsing的语法定义规则,代码量相对较大

方案3:使用AST模块构建安全执行环境(最高安全等级)

通过Python的AST模块解析表达式为抽象语法树,严格控制允许执行的节点、变量和函数,彻底避免Eval的注入风险,适合用户输入不可控的场景。

代码实现

from django.db import models
import ast
import re

class ExpressionModel(models.Model):
    input_expr = models.TextField(verbose_name="用户输入表达式")
    var_values = models.JSONField(default=dict, verbose_name="变量键值对")

    def evaluate(self):
        # 1. 将自定义IF替换为自定义函数名
        expr_str = re.sub(r'IF\(', r'my_if(', self.input_expr)
        # 2. 替换变量占位符为合法的变量引用
        expr_str = re.sub(r'\(([\w\s]+)\)', r"var_values['\1']", expr_str)
        
        # 3. 解析为AST并检查安全性
        tree = ast.parse(expr_str, mode='eval')
        self._validate_ast(tree)
        
        # 4. 定义自定义函数和执行环境
        def my_if(cond, true_val, false_val):
            return true_val if cond else false_val
        
        locals_dict = {
            'var_values': self.var_values,
            'my_if': my_if
        }
        compiled = compile(tree, '<user_expr>', 'eval')
        return eval(compiled, {'__builtins__': None}, locals_dict)

    def _validate_ast(self, tree):
        allowed_nodes = {
            ast.Expression, ast.BinOp, ast.Add, ast.Sub, ast.Mult, ast.Div,
            ast.Name, ast.Constant, ast.Call
        }
        allowed_funcs = {'my_if'}
        allowed_vars = {'var_values'}

        class SafeVisitor(ast.NodeVisitor):
            def visit_Call(self, node):
                if isinstance(node.func, ast.Name) and node.func.id not in allowed_funcs:
                    raise ValueError(f"禁止使用未授权函数: {node.func.id}")
                self.generic_visit(node)
            
            def visit_Name(self, node):
                if node.id not in allowed_vars:
                    raise ValueError(f"禁止使用未授权变量: {node.id}")
                self.generic_visit(node)
            
            def generic_visit(self, node):
                if type(node) not in allowed_nodes:
                    raise ValueError(f"禁止使用的语法结构: {type(node).__name__}")
                super().generic_visit(node)
        
        visitor = SafeVisitor()
        visitor.visit(tree)

优缺点

  • 优点:最高安全等级,严格控制所有执行内容,支持自定义函数扩展,无注入风险
  • 缺点:需要理解AST节点结构,代码相对复杂

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 14:45:40