Source code for dcolumn.dcolumns.manager

# -*- coding: utf-8 -*-
#
# dcolumn/dcolumns/manager.py
#

"""
The DColumn manager uses the Borg pattern.
"""
__docformat__ = "restructuredtext en"

import logging
import warnings

from django.conf import settings
from django.utils.translation import gettext, gettext_lazy as _
from django.db.models.query import QuerySet

log = logging.getLogger('dcolumns.dcolumns.manager')


[docs]class DynamicColumnManager(object): """ This class manages the dynamic columns. """ __shared_state = {} _relations = [] _relation_numbers = set() _choice_map = {} _css_containers = [] _css_container_map = {} def __init__(self): """ Constructor creates the Borg pattern. """ self.__dict__ = self.__shared_state
[docs] def register_choice(self, choice, relation_num, field): """ Register choice field types. These can be Foreign Key or multiple-choice non-database columns. ManyToMany is not supported at this time. :param choice: This can be either a Django model or sudo model class. A choice class mimics a model class so that this manager can work with them as if they were Django model classes. :type choice: ClassType :param relation_num: A numeric identifier for the `choice` used as the HTML select option value. :type relation_num: int :param field: A field from the model or choice object used as the HTML select option text. :type field: str """ if relation_num in self._relation_numbers: msg = ("Invalid relation number {} is already used. [choice: {}, " "field: {}]").format(relation_num, choice, field) log.critical(msg) raise ValueError(msg) if (not hasattr(choice, 'objects') or not hasattr(choice.objects, 'model_objects')): msg = ("Invalid 'choice' object '{}', must have a " "'model_objects' manager class method.").format(choice) log.critical(msg) raise AttributeError(msg) self._check_field(choice, field) self._relation_numbers.add(relation_num) self._relations.append((relation_num, choice.__name__)) self._choice_map[choice.__name__] = (choice, field) log.debug("choice: %s, relation_num: %s, field: %s, relations: %s, " "choice_map: %s", choice, relation_num, field, self._relations, self._choice_map)
def _unregister_choice(self, choice): """ Unregister choice from the manager. .. note:: This is an undocumented method and should only be used for testing. :param choice: This is a ``CHOICE`` object not a string. :type choice: ``CHOICE`` object """ _choice, field = self._choice_map.get(choice.__name__, (None, None)) relation_num = dict([(v, k) for k, v in self._relations]).get( choice.__name__) if _choice and field and relation_num: self._choice_map.pop(_choice.__name__) self._relations.remove((relation_num, _choice.__name__)) self._relation_numbers.remove(relation_num) else: msg = "Tried to remove an invalid choice object {}.".format(choice) log.error(msg) raise ValueError(msg) def _check_field(self, choice, field): """ Test that the specified field is a member object on the instantiated class object. :param choice: This can be either a Django model or choice object. A choice object mimics a model class so that this manager can work with them as if they were Django models. :type choice: ClassType :param field: A field from the model or choice object used as the HTML select option text. :raises AttributeError: If ``field`` is not an attribute of ``choice``. """ obj = choice() if not hasattr(obj, field): msg = "The '{}' object does not have the field '{}'".format( choice.__name__, field) log.critical(msg) raise AttributeError(msg) @property def choice_relations(self): """ A property that returns the HTML select option choices. :rtype: A ``list`` of the choices. """ if len(self._relations) and self._relations[0][0] != 0: self._relations.sort(key=lambda x: x[1].lower()) self._relations.insert(0, (0, _("Choose a Relation"))) return self._relations @property def choice_relation_map(self): """ A property that returns a dict (map) of the choices. :rtype: A dict of the choices. ``{num, <object name>, ...}`` The key is the number given when added with the register_choice method. The value is the string representation of the choice object. """ return dict(self._relations) @property def choice_map(self): """ A property that returns a dict (map). This property is used internally by `dcolumn`. :rtype: A dict where the key is the choice name (model or choice type) and the value is a tuple of the model/choice object and the field. """ return self._choice_map
[docs] def register_css_containers(self, container_list): """ Register the CSS container objects. This method is usually called in the settings file. .. note:: The format that is put in the settings should be something like this: ``((<template var>, <CSS class name>), ...)`` Example: ``(('top', 'container_top'), ('bottom', 'container_bottom'))`` :param container_list: A list of the CSS classes that will determine the location on the page of the various dynamic columns. :type container_list: list or tuple :raises TypeError: If ``container_list`` is not a ``list`` or ``tuple``. """ if isinstance(container_list, (list, tuple)): if len(container_list) <= 0: msg = ("Must supply at least one CSS container. The format " "should be in this formats: (('top', 'container_top'), " "('bottom', 'container_bottom'))") log.critical(msg) raise TypeError(msg) self._css_container_map.clear() self._css_containers += list(container_list) self._css_container_map.update(dict(self._css_containers)) else: msg = ("Invalid container_list type '{}', should be either a list " "of tuple.").format(type(container_list)) log.critical(msg) raise TypeError(msg)
def _unregester_css_containers(self, container_list): """ Unregister css containers from the manager. .. note:: This is an undocumented method and should only be used for testing. :param container_list: A list of lists as in: (('top', 'container_top'), ('bottom', 'container_bottom')). """ for item in container_list: self._css_containers.remove(item) self._css_container_map.clear() self._css_container_map.update(dict(self._css_containers)) @property def css_containers(self): """ A property that returns a list of tuples where the key is the template variable name and the CSS container class. :rtype: A list of tuples where the tuple is ``(<template var>, <CSS class name>)``. """ return self._css_containers @property def css_container_map(self): """ A property that returns a dict where the key is the CSS container number and the value is the CSS class or id. This property should be used in templates to designate location in the HTML. :rtype: A ``dict`` of the CSS container classes. """ return self._css_container_map
[docs] def get_collection_name(self, model_name): """ Gets the ``ColumnCollection`` name of the model name. The model name can be all lowercase without underscores or the camel case class name. :param model_name: The dynamic column model name. :type model_name: str :rtype: A str representing the ``ColumnCollection`` name. :raises ValueError: If a ``ColumnCollection`` objects could not be found. """ result = None relation_names = [ key for key, name in self.get_related_object_names(choose=False)] name = model_name.lower() if name in relation_names: result = name else: msg = _("The model '{}' must be in this list '{}' to be a valid " "collection name.").format(name, relation_names) log.error(gettext(msg)) raise ValueError(msg) return result
@property def api_auth_state(self): """ Gets the value of settings.DYNAMIC_COLUMNS.INACTIVATE_API_AUTH. The state of this variable determines if the backend AJAX API needs authorization. The defaults is ``False`` indicating that authorization is active. :rtype: ``True`` or ``False``. """ if hasattr(settings, 'DYNAMIC_COLUMNS'): result = settings.DYNAMIC_COLUMNS.get('INACTIVATE_API_AUTH', False) else: result = False return result
[docs] def get_relation_model_field(self, relation): """ Gets the model class object and the field used in the HTML select option text value. e.g. (example_site.books.models.Author, 'name') :param relation: The value in the ``DynamicColumn`` relation field. :type relation: int :rtype: The model object and field used in the HTML select option text value. """ return self.choice_map.get(self.choice_relation_map.get(relation), (None, None))
dcolumn_manager = DynamicColumnManager()