django-categories/categories/registration.py
2024-04-17 12:18:12 +02:00

177 lines
6.8 KiB
Python

"""
These functions handle the adding of fields to other models.
"""
from typing import Optional, Type, Union
from collections.abc import Iterable
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
from django.db.models import CASCADE, ForeignKey, ManyToManyField
# from settings import self._field_registry, self._model_registry
from django.utils.translation import gettext_lazy as _
from . import fields
FIELD_TYPES = {
"ForeignKey": ForeignKey,
"ManyToManyField": ManyToManyField,
}
class Registry(object):
"""Keeps track of fields and models registered."""
def __init__(self):
self._field_registry = {}
self._model_registry = {}
def register_model(self, app: str, model_name, field_type: str, field_definitions: Union[str, Iterable]):
"""
Registration process for Django 1.7+.
Args:
app: app name/label
model_name: name of the model
field_definitions: a string, tuple or list of field configurations
field_type: either 'ForeignKey' or 'ManyToManyField'
Raises:
ImproperlyConfigured: For incorrect parameter types or missing model.
"""
from django.apps import apps
app_label = app
if isinstance(field_definitions, str):
field_definitions = [field_definitions]
elif not isinstance(field_definitions, Iterable):
raise ImproperlyConfigured(
_("Field configuration for %(app)s should be a string or iterable") % {"app": app}
)
if field_type not in ("ForeignKey", "ManyToManyField"):
raise ImproperlyConfigured(_('`field_type` must be either `"ForeignKey"` or `"ManyToManyField"`.'))
try:
if not hasattr(model_name, "_meta"):
app_config = apps.get_app_config(app)
app_label = app_config.label
model = app_config.get_model(model_name)
else:
model = model_name
model_name = model._meta.model_name
opts = model._meta
if app_label not in self._model_registry:
self._model_registry[app_label] = []
if model not in self._model_registry[app_label]:
self._model_registry[app_label].append(model)
except LookupError:
raise ImproperlyConfigured(
'Model "%(model)s" doesn\'t exist in app "%(app)s".' % {"model": model_name, "app": app}
)
if not isinstance(field_definitions, (tuple, list)):
field_definitions = [field_definitions]
for fld in field_definitions:
extra_params = {"to": "categories.Category", "blank": True}
if field_type != "ManyToManyField":
extra_params["on_delete"] = CASCADE
extra_params["null"] = True
if isinstance(fld, str):
field_name = fld
elif isinstance(fld, dict):
if "name" in fld:
field_name = fld.pop("name")
else:
continue
extra_params.update(fld)
else:
raise ImproperlyConfigured(
_("%(settings)s doesn't recognize the value of %(app)s.%(model)s")
% {"settings": "CATEGORY_SETTINGS", "app": app, "model": model_name}
)
registry_name = ".".join([app_label, model_name.lower(), field_name])
if registry_name in self._field_registry:
continue
try:
opts.get_field(field_name)
except FieldDoesNotExist:
self._field_registry[registry_name] = FIELD_TYPES[field_type](**extra_params)
self._field_registry[registry_name].contribute_to_class(model, field_name)
def register_m2m(self, model, field_name: str = "categories", extra_params: Optional[dict] = None):
"""Register a field name to the model as a many to many field."""
extra_params = extra_params or {}
return self._register(model, field_name, extra_params, fields.CategoryM2MField)
def register_fk(self, model, field_name: str = "category", extra_params: Optional[dict] = None):
"""Register a field name to the model as a foreign key."""
extra_params = extra_params or {}
return self._register(model, field_name, extra_params, fields.CategoryFKField)
def _register(
self,
model,
field_name: str,
extra_params: Optional[dict] = None,
field: Type = fields.CategoryFKField,
):
"""Does the heavy lifting for registering a field to a model."""
app_label = model._meta.app_label
extra_params = extra_params or {}
registry_name = ".".join((app_label, model.__name__, field_name)).lower()
if registry_name in self._field_registry:
return # raise AlreadyRegistered
opts = model._meta
try:
opts.get_field(field_name)
except FieldDoesNotExist:
if app_label not in self._model_registry:
self._model_registry[app_label] = []
if model not in self._model_registry[app_label]:
self._model_registry[app_label].append(model)
self._field_registry[registry_name] = field(**extra_params)
self._field_registry[registry_name].contribute_to_class(model, field_name)
registry = Registry()
def _process_registry(registry, call_func):
"""
Given a dictionary, and a registration function, process the registry.
"""
from django.apps import apps
from django.core.exceptions import ImproperlyConfigured
for key, value in list(registry.items()):
model = apps.get_model(*key.split("."))
if model is None:
raise ImproperlyConfigured(_("%(key)s is not a model") % {"key": key})
if isinstance(value, (tuple, list)):
for item in value:
if isinstance(item, str):
call_func(model, item)
elif isinstance(item, dict):
field_name = item.pop("name")
call_func(model, field_name, extra_params=item)
else:
raise ImproperlyConfigured(
_("%(settings)s doesn't recognize the value of %(key)s")
% {"settings": "CATEGORY_SETTINGS", "key": key}
)
elif isinstance(value, str):
call_func(model, value)
elif isinstance(value, dict):
field_name = value.pop("name")
call_func(model, field_name, extra_params=value)
else:
raise ImproperlyConfigured(
_("%(settings)s doesn't recognize the value of %(key)s")
% {"settings": "CATEGORY_SETTINGS", "key": key}
)