Django 2.0嵌套Q对象过滤关联模型及序列化存储的实现疑问
你想要的组合过滤逻辑完全可行,但需要调整一下写法——直接用Q(tires=my_tire_filter)是不对的,Django的关联查询需要通过双下划线(__)或者子查询的方式,把Tire的过滤条件关联到Car模型上。下面是具体实现方案:
正确组合Q对象的两种方式
方式1:转换Tire过滤条件为Car关联查询格式
如果你想保持纯Q对象的组合,可以把my_tire_filter的条件重新包装成带有tires__前缀的Q对象,这样就能直接和Car的过滤条件组合:
# 把Tire的Q对象转换为Car关联字段的格式 tire_q_for_car = Q() for child in my_tire_filter.children: if isinstance(child, tuple): field, value = child # 给每个Tire字段添加tires__前缀 tire_q_for_car.add(Q(**{f'tires__{field}': value}), my_tire_filter.connector) # 组合Car自身过滤条件和转换后的Tire过滤条件 my_car_filter = my_filter & tire_q_for_car
之后就可以用这个纯Q对象查询:
the_cars = Car.objects.filter(my_car_filter)
方式2:使用子查询关联Tire过滤
如果你不想手动转换字段前缀,也可以直接用子查询的方式生成Q对象,写法更简洁,同样是纯Q对象:
my_car_filter = my_filter & Q(tires__in=Tire.objects.filter(my_tire_filter))
这个写法会对应SQL中的子查询,完全满足你序列化存储的需求。
Q对象的序列化存储方案
Q对象本身可以被序列化,但直接用pickle存储到数据库存在安全风险(恶意反序列化可能导致问题)。更安全的方式是手动序列化Q对象的核心结构:
Q对象有三个核心属性:
children: 存储过滤条件的元组或子Q对象connector: 条件连接符(AND/OR,对应&/|)negated: 是否取反(对应~)
你可以把这些属性序列化为JSON格式,存储到数据库的TextField中,需要使用时再反序列化为Q对象。示例代码如下:
序列化函数
import json def serialize_q(q_obj): def _serialize(child): if isinstance(child, Q): return { 'type': 'Q', 'children': [_serialize(c) for c in child.children], 'connector': child.connector, 'negated': child.negated } else: # 处理字段-值的条件元组 return {'type': 'condition', 'field': child[0], 'value': child[1]} return json.dumps(_serialize(q_obj))
反序列化函数
def deserialize_q(json_str): def _deserialize(data): if data['type'] == 'Q': q = Q() q.children = [_deserialize(c) for c in data['children']] q.connector = data['connector'] q.negated = data['negated'] return q else: return (data['field'], data['value']) data = json.loads(json_str) return _deserialize(data)
通过这种方式,你可以安全地将Q对象序列化后存储到数据库,需要查询时再反序列化出来直接使用。
内容的提问来源于stack exchange,提问作者janoliver
相关产品推荐
相关产品推荐

