Use lazy='joined' instead of lazy='selectin' on Entity.parent.

That's the best way to ensure that all the columns are fetched eagerly and
prevent errors later when trying to access lazily loaded attributes outside
of the session/thread.
This commit is contained in:
Fabio Manganiello 2023-03-10 12:01:23 +01:00
parent 8ccf3e804d
commit f45e47363d
Signed by untrusted user: blacklight
GPG key ID: D90FBA7F76362774

View file

@ -3,10 +3,10 @@ import json
import pathlib import pathlib
import types import types
from datetime import datetime from datetime import datetime
from dateutil.tz import tzutc
from typing import Callable, Final, Mapping, Set, Type, Tuple, Any
import pkgutil import pkgutil
from typing import Callable, Dict, Final, Set, Type, Tuple, Any
from dateutil.tz import tzutc
from sqlalchemy import ( from sqlalchemy import (
Boolean, Boolean,
Column, Column,
@ -24,7 +24,8 @@ from sqlalchemy.orm import ColumnProperty, Mapped, backref, relationship
from platypush.common.db import Base from platypush.common.db import Base
from platypush.message import JSONAble from platypush.message import JSONAble
entities_registry: Mapping[Type['Entity'], Mapping] = {} EntityRegistryType = Dict[str, Type['Entity']]
entities_registry: EntityRegistryType = {}
_import_error_ignored_modules: Final[Set[str]] = {'bluetooth'} _import_error_ignored_modules: Final[Set[str]] = {'bluetooth'}
""" """
@ -78,7 +79,7 @@ if 'entity' not in Base.metadata:
'Entity', 'Entity',
remote_side=[id], remote_side=[id],
uselist=False, uselist=False,
lazy='selectin', lazy='joined',
post_update=True, post_update=True,
backref=backref( backref=backref(
'children', 'children',
@ -104,7 +105,7 @@ if 'entity' not in Base.metadata:
@classmethod # type: ignore @classmethod # type: ignore
@property @property
def columns(cls) -> Tuple[ColumnProperty]: def columns(cls) -> Tuple[ColumnProperty, ...]:
inspector = schema_inspect(cls) inspector = schema_inspect(cls)
return tuple(inspector.mapper.column_attrs) return tuple(inspector.mapper.column_attrs)
@ -138,12 +139,21 @@ if 'entity' not in Base.metadata:
return {col.key: self._serialize_value(col) for col in self.columns} return {col.key: self._serialize_value(col) for col in self.columns}
def __repr__(self): def __repr__(self):
"""
Same as :meth:`.__str__`.
"""
return str(self) return str(self)
def __str__(self): def __str__(self):
"""
:return: A JSON-encoded representation of the entity.
"""
return json.dumps(self.to_dict()) return json.dumps(self.to_dict())
def __setattr__(self, key, value): def __setattr__(self, key, value):
"""
Serializes the new value before assigning it to an attribute.
"""
matching_columns = [c for c in self.columns if c.expression.name == key] matching_columns = [c for c in self.columns if c.expression.name == key]
if ( if (
@ -212,11 +222,17 @@ def _discover_entity_types():
entities_registry[obj] = {} entities_registry[obj] = {}
def get_entities_registry(): def get_entities_registry() -> EntityRegistryType:
"""
:returns: A copy of the entities registry.
"""
return entities_registry.copy() return entities_registry.copy()
def init_entities_db(): def init_entities_db():
"""
Initializes the entities database.
"""
from platypush.context import get_plugin from platypush.context import get_plugin
_discover_entity_types() _discover_entity_types()