Source code for save_to_db.core.item_cls_manager
from .utils.item_collection import ItemCollection
[docs]class ItemClsManager(ItemCollection):
"""This class manages all known item classes
(see :py:class:`~.item.Item` base class). Normally only one instance of
this class is used.
:param autogenerate: If `True` then getting a missing item by model class
will generate a new item class with all parameters auto configured.
"""
def __init__(self, autogenerate=False):
super().__init__(collection_id=None)
self.autogenerate = autogenerate
[docs] def get_by_path(self, path, relative_to=None):
"""Gets item classes using `path` (module path and class name separated
by dot).
:param path: Item path. Multiple initial dots allowed, e.g.
`'...some_module.SomeClass'`.
:param relative_to: In case `path` argument is relative, this arguments
contains a path it is relative to.
:return: List of item classes.
"""
result = []
# if path is relative or just a class name
if path.startswith(".") or "." not in path:
if path.startswith("."):
if relative_to is None:
raise Exception(
"Relative path not allowed without setting "
"`relative_to` argument."
)
stripped_path = path.lstrip(".")
dot_count = len(path) - len(stripped_path)
if dot_count > 1:
path_parts = relative_to.split(".")[: -(dot_count - 1)]
relative_to = ".".join(path_parts)
else:
stripped_path = path
if relative_to:
path = "{}.{}".format(relative_to, stripped_path)
search_by_name = "." not in path
for item_cls in self.collection:
# by name
if search_by_name:
if item_cls.__name__ == path:
result.append(item_cls)
continue
# by path
item_path = "{}.{}".format(item_cls.__module__, item_cls.__name__)
if item_path == path:
result.append(item_cls)
return result
def __autogenerate_item_cls(self, model_cls):
from .item import Item # circular reference
return Item._Auto(
model_cls,
metadata={
"autogenerated_item_cls": True,
},
)
[docs] def get_by_model_cls(self, model_cls, collection_id=None):
"""Gets item classes using ORM model class.
:param model_cls: An ORM model class that an item is using to persist
data.
:param collection_id: Scope ID of the item class.
:return: List of item classes.
"""
from .scope import Scope
result = []
if collection_id:
scope = Scope.get_collection_by_id(collection_id)
else:
scope = None
for item_cls in self.collection:
if item_cls.model_cls is model_cls:
if scope:
item_cls = scope[item_cls]
result.append(item_cls)
if not result and self.autogenerate:
item_cls = self.__autogenerate_item_cls(model_cls)
if scope:
item_cls = scope[item_cls]
result.append(item_cls)
return result
[docs] def autocomplete_item_classes(self):
"""If `self.autogenerate` is `True` then auto generates missing item
classes for models classes that are needed for already registered item
classes via relations.
"""
if not self.autogenerate:
return
# generating all missing items
while True:
regestry_copy = self.collection.copy()
for item_cls in regestry_copy:
item_cls.complete_setup()
for relation in item_cls.relations.values():
relation["item_cls"].complete_setup()
if regestry_copy == self.collection:
break
def __repr__(self):
return "<{}()>".format(self.__class__.__name__)
#: Normally only this instance of :py:class:`~ItemClsManager` is used.
item_cls_manager = ItemClsManager()