django-categories/categories/registration.py

178 lines
6.8 KiB
Python
Raw Normal View History

"""
These functions handle the adding of fields to other models.
"""
from typing import Optional, Type, Union
2021-12-16 11:26:40 +00:00
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
2021-12-16 11:26:40 +00:00
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 = {}
2021-12-16 11:26:40 +00:00
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
2016-02-10 17:47:30 +00:00
if isinstance(field_definitions, str):
field_definitions = [field_definitions]
2021-12-16 11:26:40 +00:00
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
2016-02-10 17:47:30 +00:00
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)
2016-11-14 18:29:05 +00:00
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
2016-02-10 17:47:30 +00:00
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:
2016-02-10 17:47:30 +00:00
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}
)
2016-02-10 17:47:30 +00:00
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}
)