mirror of
https://github.com/jazzband/django-categories.git
synced 2026-03-16 22:30:24 +00:00
177 lines
6.8 KiB
Python
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}
|
|
)
|