如何在自定义ORM中实现类似SQLAlchemy/Peewee的where表达式传递?
实现ORM中表达式参数的核心原理
你疑惑的核心点是ORM的关键技巧:Python确实会先计算表达式,但这些表达式返回的不是布尔值,而是承载了逻辑的表达式对象。下面拆解具体实现步骤:
1. 自定义字段类:重载魔法方法
ORM里的Person.name不是普通字符串,是自定义字段类(比如Field)的实例。我们需要重载__eq__、__lt__、__or__这类魔法方法,让它们返回一个表达式节点,而非直接计算布尔结果。
示例代码:
class Field: def __init__(self, db_column): self.db_column = db_column # 对应数据库表的列名 # 重载相等运算符 def __eq__(self, value): return BinaryExpression(left=self, op="=", right=value) # 重载小于运算符 def __lt__(self, value): return BinaryExpression(left=self, op="<", right=value) # 重载大于运算符 def __gt__(self, value): return BinaryExpression(left=self, op=">", right=value)
2. 表达式对象:构建逻辑树
定义BinaryExpression类存储二元表达式的结构(左操作数、运算符、右操作数),同时重载位运算(|、&)支持多表达式组合:
class BinaryExpression: def __init__(self, left, op, right): self.left = left self.op = op self.right = right # 重载或运算符,组合表达式 def __or__(self, other): return BinaryExpression(left=self, op="OR", right=other) # 重载与运算符 def __and__(self, other): return BinaryExpression(left=self, op="AND", right=other) # 生成SQL片段的核心方法 def to_sql(self): # 递归处理嵌套表达式 if isinstance(self.left, BinaryExpression): left_sql = self.left.to_sql() elif isinstance(self.left, Field): left_sql = self.left.db_column else: # 处理普通值,比如字符串加引号 left_sql = f"'{self.left}'" if isinstance(self.right, BinaryExpression): right_sql = self.right.to_sql() elif isinstance(self.right, Field): right_sql = self.right.db_column else: right_sql = f"'{self.right}'" return f"({left_sql} {self.op} {right_sql})"
3. Query类:接收并处理表达式
where方法不需要"获取表达式本身",它接收的就是上面生成的BinaryExpression对象,调用to_sql()就能转换成SQL片段:
class Query: def __init__(self, model_cls): self.model_cls = model_cls self.where_expr = None def where(self, *expressions): # 多表达式默认用AND连接 if len(expressions) > 1: combined = expressions[0] for expr in expressions[1:]: combined = combined & expr self.where_expr = combined else: self.where_expr = expressions[0] return self # 支持链式调用 def to_sql(self): table_name = self.model_cls.__name__ sql = f"SELECT * FROM {table_name}" if self.where_expr: sql += f" WHERE {self.where_expr.to_sql()}" return sql
4. 模型类:整合字段与查询
定义基础Model类,让业务模型继承它:
class Model: @classmethod def select(cls): return Query(cls) # 业务模型示例 class Person(Model): name = Field("name") birthday = Field("birthday")
测试验证
现在可以模拟ORM的用法:
d1940 = "1940-01-01" d1960 = "1960-01-01" # 简单表达式查询 grandma_query = Person.select().where(Person.name == 'Grandma L.') print(grandma_query.to_sql()) # 输出: SELECT * FROM Person WHERE (name = 'Grandma L.') # 复杂组合表达式 range_query = Person.select().where((Person.birthday < d1940) | (Person.birthday > d1960)) print(range_query.to_sql()) # 输出: SELECT * FROM Person WHERE ((birthday < '1940-01-01') OR (birthday > '1960-01-01'))
核心总结
- 所有比较/逻辑运算都返回表达式对象,而非布尔值,这是ORM能捕获逻辑的关键
- 表达式对象可以嵌套组合,形成一棵抽象语法树
where方法只需要接收这些对象,调用其to_sql方法即可生成对应的SQL片段
内容的提问来源于stack exchange,提问作者Carl L
相关产品推荐
相关产品推荐

