erikvw/django-collect-offline

View on GitHub
django_collect_offline/site_offline_models.py

Summary

Maintainability
A
3 hrs
Test Coverage
import copy
import sys

from django.apps import apps as django_apps
from django.utils.module_loading import import_module, module_has_submodule

from . import get_offline_enabled

from .offline_model import OfflineModel


class AlreadyRegistered(Exception):
    pass


class ModelNotRegistered(Exception):
    pass


class SiteOfflineModels:
    module_name = "offline_models"
    wrapper_cls = OfflineModel
    register_historical = True

    def __init__(self):
        self.registry = {}
        self.loaded = False

    def register(self, models=None, wrapper_cls=None):
        """Registers with app_label.modelname, wrapper_cls.
        """
        if get_offline_enabled():
            self.loaded = True
            for model in models:
                model = model.lower()
                if model not in self.registry:
                    self.registry.update({model: wrapper_cls or self.wrapper_cls})
                    if self.register_historical:
                        historical_model = ".historical".join(model.split("."))
                        self.registry.update(
                            {historical_model: wrapper_cls or self.wrapper_cls}
                        )
                else:
                    raise AlreadyRegistered(
                        f"Model is already registered. Got {model}."
                    )

    def register_for_app(
        self, app_label=None, exclude_models=None, exclude_model_classes=None
    ):
        """Registers all models for this app_label.
        """
        models = []
        exclude_models = exclude_models or []
        app_config = django_apps.get_app_config(app_label)
        for model in app_config.get_models():
            if model._meta.label_lower in exclude_models:
                pass
            elif exclude_model_classes and issubclass(model, exclude_model_classes):
                pass
            else:
                models.append(model._meta.label_lower)
        self.register(models)

    def get_wrapped_instance(self, instance=None):
        """Returns a wrapped model instance.
        """
        if instance._meta.label_lower not in self.registry:
            raise ModelNotRegistered(f"{repr(instance)} is not registered with {self}.")
        wrapper_cls = self.registry.get(instance._meta.label_lower) or self.wrapper_cls
        if wrapper_cls:
            return wrapper_cls(instance)
        return instance

    def site_models(self, app_label=None):
        """Returns a dictionary of registered models.
        """
        site_models = {}
        app_configs = (
            django_apps.get_app_configs()
            if app_label is None
            else [django_apps.get_app_config(app_label)]
        )
        for app_config in app_configs:
            model_list = [
                model
                for model in app_config.get_models()
                if model._meta.label_lower in self.registry
            ]
            if model_list:
                model_list.sort(key=lambda m: m._meta.verbose_name)
                site_models.update({app_config.name: model_list})
        return site_models

    def autodiscover(self, module_name=None):
        module_name = module_name or self.module_name
        sys.stdout.write(" * checking for models to register ...\n")
        for app in django_apps.app_configs:
            try:
                mod = import_module(app)
                try:
                    before_import_registry = copy.deepcopy(self.registry)
                    import_module(f"{app}.{module_name}")
                    sys.stdout.write(f" * registered models from '{app}'.\n")
                except Exception as e:
                    if f"No module named '{app}.{module_name}'" not in str(e):
                        raise
                    self.registry = before_import_registry
                    if module_has_submodule(mod, module_name):
                        raise
            except ImportError:
                pass


site_offline_models = SiteOfflineModels()