У меня есть устаревшая кодовая база Python 3 с использованием SQLAlchemy 1.1, у которой по «причинам» есть класс:
class jsonbool(str):
def __bool__(self):
return True if self == 'true' else False
Этот класс используется в выражении фильтра SQLAlchemy, например. query.filter(SomeTable.ABooleanColumn == anInstanceOfjsonbool).all()
. Это нормально работало в 1.1, поскольку использовалось строковое представление типа jsonbool
(например, true
или false
).
В SQLAlchemy 1.2 на их стороне была добавлена дополнительная проверка, чтобы предотвратить преобразование некоторых типов в логические. То, что работало выше, теперь не работает с sqlalchemy.exc.StatementError: (builtins.TypeError) Not a boolean value: 'false'
(false
на самом деле является экземпляром jsonbool
).
Я подумал исправить это с помощью SQLAlchemy TypeDecorator, который позволил бы мне преобразовать параметр в логическое значение при привязке в выражении. Мой прототип был просто для того, чтобы заставить работать украшение нестандартного типа, с
import sqlalchemy.types as types
class jsonbool(str, types.TypeDecorator):
impl = types.Boolean
def __bool__(self):
return True if self == 'true' else False
Увы, это и все подобное, что я пробую, приводит к AttributeError: 'Boolean' object has no attribute 'self_group'
(где s/Boolean/WhateverImplIPick
) при попытке выполнить запрос. Я также пробовал использовать UserDefinedType
с тем же результатом.
Как я могу изменить поведение типа jsonbool
, когда я использую его как часть выражения SQLAlchemy?
TypeDecorator
- это тип SQLAlchemy, похожий на String
или Boolean
, экземпляры которого используются для объявления типа столбца или выражения, как в
foo = Column(String, ...)
bar = Column(jsonbool, ...)
Использование такого типа в качестве значения не имеет смысла, поэтому точно так же, как str
отделен от String
, вам нужен класс JsonBool
, отдельный от jsonbool
, например:
class JsonBool(TypeDecorator):
impl = Boolean
def process_bind_param(self, value, dialect):
return value == "true"
def process_result_value(self, value, dialect):
return jsonbool("true") if value else jsonbool("false")
Конечно, вам нужно будет изменить определение вашего SomeTable.ABooleanColumn
, чтобы использовать этот тип:
ABooleanColumn = Column(JsonBool, ...)
Это может быть сложной задачей для вашей базы кода, и в этом случае вы можете заставить SQLAlchemy выполнять кастомная компиляция для объектов jsonbool
:
class jsonbool(str, ColumnElement):
def __bool__(self):
return True if self == 'true' else False
@compiles(jsonbool)
def _compile_jsonbool(element, compiler, **kwargs):
if element:
return compiler.visit_true(None)
else:
return compiler.visit_false(None)
Похоже, я совершенно неправильно понял понятие Type / TypeDecorator. Огромное спасибо!