[英]How to select different type of columns dynamically to use in where clause?
I have a following table in sqlalchemy: 我在sqlalchemy中有下表:
class FieldType(enum.Enum):
INT_FIELD = 0
FLOAT_FIELD = 1
STRING_FIELD = 2
class EAVTable(Base):
__tablename__ = 'EAVTable'
field_name = Column(Stirng, primary_key=True)
field_type = Column(Enum(FieldType))
int_field = Column(Integer)
float_field = Column(Float)
string_field = Column(String)
This is to model the EAV model which fits my business purpose. 这是为了建立适合我的业务目的的EAV模型 。
Now to use it easily in the code I have the following hybrid_property
. 现在要在代码中轻松使用它,我有以下
hybrid_property
。
@hybrid_propderty
def value(self):
if self.field_type == FieldType.INT_FIELD:
return self.int_field
...
@value.setter
def value(self, value):
if type(value) == int:
self.field_type = FieldType.INT_FIELD
self.int_field = value
...
This works fine when I try to get and set the fields in Python code. 当我尝试获取并设置Python代码中的字段时,此方法效果很好。 But I still have a problem:
但是我仍然有一个问题:
session.query(EAVTable).filter(EAVTable.value == 123)
This does not work out of the box but I had an idea of using hybrid.expression where we use a case statement: 这不是开箱即用的,但是我有一个在其中使用case语句的情况下使用hybrid.expression的想法:
@value.expression
def value(cls):
return case(
[
(cls.field_type == FieldType.INT_FIELD, cls.int_field),
(cls.field_type == FieldType.FLOAT_FIELD, cls.float_field),
...
]
)
This in theory works, for example, the SQL generated for query session.query(EAVTable.value = 123
looks like: 从理论上讲这是
session.query(EAVTable.value = 123
,例如,为查询session.query(EAVTable.value = 123
生成的SQL看起来像:
select * from where case
when field_type = INT_FIELD then int_field
when field_type = FLOAT_FIELD then float_field
when field_type = STRING_FIELD then string_field
end = 123;
Which semantically looks like what I like, but later I find that the case
expression requires all the cases have the same type, or they are cast into the same type. 从语义上看我喜欢哪种情况,但是后来我发现
case
表达式要求所有case具有相同的类型,或者将它们转换为相同的类型。
I understand this is a requirement from the SQL language and has nothing to do with sqlachemy, but for more seasoned sqlalchemy user, is there any easy way to do what I want to achieve? 我了解这是SQL语言的要求,与sqlachemy无关,但是对于经验丰富的sqlalchemy用户,有什么简单的方法可以实现我想要的目标? Is there a way to walk around this constraint?
有没有办法解决这个限制?
You could move the comparison inside the CASE
expression using a custom comparator : 您可以使用自定义比较器在
CASE
表达式内移动比较 :
from sqlalchemy.ext.hybrid import Comparator
class PolymorphicComparator(Comparator):
def __init__(self, cls):
self.cls = cls
def __clause_element__(self):
# Since SQL doesn't allow polymorphism here, don't bother trying.
raise NotImplementedError(
f"{type(self).__name__} cannot be used as a clause")
def operate(self, op, other):
cls = self.cls
return case(
[
(cls.field_type == field_type, op(field, other))
for field_type, field in [
(FieldType.INT_FIELD, cls.int_field),
(FieldType.FLOAT_FIELD, cls.float_field),
(FieldType.STRING_FIELD, cls.string_field),
]
],
else_=False
)
class EAVTable(Base):
...
# This replaces @value.expression
@value.comparator
def value(cls):
return PolymorphicComparator(cls)
This way the common type is just boolean. 这样,普通类型就是布尔值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.