From 2b878a2ff70e561b68dfb24ffb6a9eba8e4373c4 Mon Sep 17 00:00:00 2001 From: Ed-XCF Date: Thu, 9 Dec 2021 20:31:19 +0800 Subject: [PATCH 1/7] to dict support hybrid_property and add Comparator --- bali/db/comparator.py | 19 +++++++++++++++++++ bali/db/connection.pyi | 6 +++--- bali/db/models.py | 16 +++++++++++++--- bali/db/operators.py | 13 +++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 bali/db/comparator.py diff --git a/bali/db/comparator.py b/bali/db/comparator.py new file mode 100644 index 0000000..2200675 --- /dev/null +++ b/bali/db/comparator.py @@ -0,0 +1,19 @@ +from collections import defaultdict + +from sqlalchemy import case +from sqlalchemy.ext.hybrid import Comparator +from sqlalchemy.util.langhelpers import dictlike_iteritems + + +class CaseComparator(Comparator): + def __init__(self, whens, expression): + super().__init__(expression) + self.whens, self.reversed_whens = dictlike_iteritems(whens), defaultdict(list) + for k, v in self.whens: + self.reversed_whens[v].append(k) + + def __clause_element__(self): + return case(self.whens, self.expression) + + def __eq__(self, other): + return self.expression.__clause_element__().in_(self.reversed_whens[other]) diff --git a/bali/db/connection.pyi b/bali/db/connection.pyi index 2f2891f..e7dcb37 100644 --- a/bali/db/connection.pyi +++ b/bali/db/connection.pyi @@ -17,11 +17,11 @@ class BaseModel: updated_time: _DateTimeField is_active: _BooleanField - def _asdict(self) -> Dict[str, Any]: ... + def _asdict(self, include_hybrid_property=False) -> Dict[str, Any]: ... - def to_dict(self) -> Dict[str, Any]: ... + def to_dict(self, include_hybrid_property=False) -> Dict[str, Any]: ... - def dict(self) -> Dict[str, Any]: ... + def dict(self, include_hybrid_property=False) -> Dict[str, Any]: ... @classmethod def exists(cls: Type[_M], **attrs) -> bool: ... diff --git a/bali/db/models.py b/bali/db/models.py index 0c4a4eb..aa7db0b 100644 --- a/bali/db/models.py +++ b/bali/db/models.py @@ -5,9 +5,12 @@ import pytz from sqlalchemy import Column, DateTime, Boolean from sqlalchemy.exc import IntegrityError, SQLAlchemyError +from sqlalchemy.orm.attributes import InstrumentedAttribute from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.sql.functions import func from sqlalchemy.types import TypeDecorator +from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy.inspection import inspect from ..utils import timezone @@ -95,10 +98,17 @@ def delete(self): db.session.delete(self) db.session.commit() if context_auto_commit.get() else db.session.flush() - def _asdict(self): - return {c.name: getattr(self, c.name, None) for c in self.__table__.columns} + def to_dict(self, include_hybrid_property=False): + output_fields = [] + for i in inspect(type(self)).all_orm_descriptors: + if isinstance(i, InstrumentedAttribute): + output_fields.append(i.key) + elif isinstance(i, hybrid_property) and include_hybrid_property: + output_fields.append(i.__name__) - dict = to_dict = _asdict + return {i: getattr(self, i, None) for i in output_fields} + + dict = _asdict = to_dict @classmethod def count(cls, **attrs) -> int: diff --git a/bali/db/operators.py b/bali/db/operators.py index 252f7f7..dd72c7e 100644 --- a/bali/db/operators.py +++ b/bali/db/operators.py @@ -7,6 +7,7 @@ from ..exceptions import OperatorModelError OPERATOR_SPLITTER = '__' +REVERSER = "-" OPERATORS = { 'isnull': lambda c, v: (c == None) if v else (c != None), # noqa @@ -68,3 +69,15 @@ def get_filters_expr(cls, **filters): expressions.append(op(column, value)) return expressions + + +def dj_lookup_to_sqla(expression: str) -> Tuple: + col_name, op_name = expression, "exact" + if OPERATOR_SPLITTER in col_name: + col_name, op_name = col_name.rsplit(OPERATOR_SPLITTER, 1) + return OPERATORS[op_name], col_name + + +def dj_ordering_to_sqla(expression: str): + wrapper = desc if expression.startswith(REVERSER) else asc + return wrapper(expression.lstrip(REVERSER)) From a60f97b93bc0781a181bad61c425ff8786a3c779 Mon Sep 17 00:00:00 2001 From: Ed-XCF Date: Fri, 10 Dec 2021 10:40:32 +0800 Subject: [PATCH 2/7] user super __clause_element__ --- bali/db/comparator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bali/db/comparator.py b/bali/db/comparator.py index 2200675..b17ee20 100644 --- a/bali/db/comparator.py +++ b/bali/db/comparator.py @@ -16,4 +16,4 @@ def __clause_element__(self): return case(self.whens, self.expression) def __eq__(self, other): - return self.expression.__clause_element__().in_(self.reversed_whens[other]) + return super().__clause_element__().in_(self.reversed_whens[other]) From b94e6955131aefe1fba54081238488902d10598f Mon Sep 17 00:00:00 2001 From: Ed-XCF Date: Fri, 10 Dec 2021 10:49:12 +0800 Subject: [PATCH 3/7] change control mode --- bali/db/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bali/db/models.py b/bali/db/models.py index aa7db0b..e5eba82 100644 --- a/bali/db/models.py +++ b/bali/db/models.py @@ -41,6 +41,7 @@ def process_bind_param(self, value, _): def get_base_model(db): class BaseModel(db.Model): __abstract__ = True + __asdict_include_hybrid_property__ = False created_time = Column(DateTime, default=datetime.utcnow) updated_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) @@ -98,17 +99,17 @@ def delete(self): db.session.delete(self) db.session.commit() if context_auto_commit.get() else db.session.flush() - def to_dict(self, include_hybrid_property=False): + def _asdict(self): output_fields = [] for i in inspect(type(self)).all_orm_descriptors: if isinstance(i, InstrumentedAttribute): output_fields.append(i.key) - elif isinstance(i, hybrid_property) and include_hybrid_property: + elif isinstance(i, hybrid_property) and self.__asdict_include_hybrid_property__: output_fields.append(i.__name__) return {i: getattr(self, i, None) for i in output_fields} - dict = _asdict = to_dict + dict = to_dict = _asdict @classmethod def count(cls, **attrs) -> int: From 839aa3b346035c48d28d740d29d0f10c2f991de6 Mon Sep 17 00:00:00 2001 From: Ed-XCF Date: Fri, 10 Dec 2021 10:51:06 +0800 Subject: [PATCH 4/7] change control mode --- bali/db/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bali/db/models.py b/bali/db/models.py index e5eba82..e9339ec 100644 --- a/bali/db/models.py +++ b/bali/db/models.py @@ -99,12 +99,12 @@ def delete(self): db.session.delete(self) db.session.commit() if context_auto_commit.get() else db.session.flush() - def _asdict(self): + def _asdict(self, include_hybrid_property=__asdict_include_hybrid_property__): output_fields = [] for i in inspect(type(self)).all_orm_descriptors: if isinstance(i, InstrumentedAttribute): output_fields.append(i.key) - elif isinstance(i, hybrid_property) and self.__asdict_include_hybrid_property__: + elif isinstance(i, hybrid_property) and include_hybrid_property: output_fields.append(i.__name__) return {i: getattr(self, i, None) for i in output_fields} From f80563b50cd3cd0b108d55d409caeb71ce9ccd01 Mon Sep 17 00:00:00 2001 From: Ed-XCF Date: Fri, 10 Dec 2021 10:55:09 +0800 Subject: [PATCH 5/7] change control mode --- bali/db/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bali/db/models.py b/bali/db/models.py index e9339ec..8ef549f 100644 --- a/bali/db/models.py +++ b/bali/db/models.py @@ -41,7 +41,7 @@ def process_bind_param(self, value, _): def get_base_model(db): class BaseModel(db.Model): __abstract__ = True - __asdict_include_hybrid_property__ = False + __asdict_include_hybrid_properties__ = False created_time = Column(DateTime, default=datetime.utcnow) updated_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) @@ -99,12 +99,12 @@ def delete(self): db.session.delete(self) db.session.commit() if context_auto_commit.get() else db.session.flush() - def _asdict(self, include_hybrid_property=__asdict_include_hybrid_property__): + def _asdict(self, include_hybrid_properties=__asdict_include_hybrid_properties__): output_fields = [] for i in inspect(type(self)).all_orm_descriptors: if isinstance(i, InstrumentedAttribute): output_fields.append(i.key) - elif isinstance(i, hybrid_property) and include_hybrid_property: + elif isinstance(i, hybrid_property) and include_hybrid_properties: output_fields.append(i.__name__) return {i: getattr(self, i, None) for i in output_fields} From 9a87ef5ca029ca4566be45c5526f7e337987a566 Mon Sep 17 00:00:00 2001 From: Ed-XCF Date: Fri, 10 Dec 2021 10:57:28 +0800 Subject: [PATCH 6/7] complete pyi file --- bali/db/connection.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bali/db/connection.pyi b/bali/db/connection.pyi index e7dcb37..cc35679 100644 --- a/bali/db/connection.pyi +++ b/bali/db/connection.pyi @@ -17,11 +17,11 @@ class BaseModel: updated_time: _DateTimeField is_active: _BooleanField - def _asdict(self, include_hybrid_property=False) -> Dict[str, Any]: ... + def _asdict(self, include_hybrid_properties=False) -> Dict[str, Any]: ... - def to_dict(self, include_hybrid_property=False) -> Dict[str, Any]: ... + def to_dict(self, include_hybrid_properties=False) -> Dict[str, Any]: ... - def dict(self, include_hybrid_property=False) -> Dict[str, Any]: ... + def dict(self, include_hybrid_properties=False) -> Dict[str, Any]: ... @classmethod def exists(cls: Type[_M], **attrs) -> bool: ... From 0787ab71b217dfa68fef8454664f7f987b0344ff Mon Sep 17 00:00:00 2001 From: Ed-XCF Date: Fri, 10 Dec 2021 10:58:08 +0800 Subject: [PATCH 7/7] complete pyi file --- bali/db/connection.pyi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bali/db/connection.pyi b/bali/db/connection.pyi index cc35679..fcc9a47 100644 --- a/bali/db/connection.pyi +++ b/bali/db/connection.pyi @@ -13,15 +13,16 @@ _BooleanField = Union[bool, Column[Optional[bool]]] class BaseModel: __abstract__ = True + __asdict_include_hybrid_properties__ = False created_time: _DateTimeField updated_time: _DateTimeField is_active: _BooleanField - def _asdict(self, include_hybrid_properties=False) -> Dict[str, Any]: ... + def _asdict(self, include_hybrid_properties=__asdict_include_hybrid_properties__) -> Dict[str, Any]: ... - def to_dict(self, include_hybrid_properties=False) -> Dict[str, Any]: ... + def to_dict(self, include_hybrid_properties=__asdict_include_hybrid_properties__) -> Dict[str, Any]: ... - def dict(self, include_hybrid_properties=False) -> Dict[str, Any]: ... + def dict(self, include_hybrid_properties=__asdict_include_hybrid_properties__) -> Dict[str, Any]: ... @classmethod def exists(cls: Type[_M], **attrs) -> bool: ...