21ff62dd32
Changed api for main menu entries. Enhanced http error pages using a classed based views. Moved dashboard and select widgets view from projector to core app. Also some small clean ups.
108 lines
3.9 KiB
Python
108 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
|
|
class SignalConnectMetaClass(type):
|
|
"""
|
|
Metaclass to connect the children of a base class to a Django signal.
|
|
|
|
Classes must have a signal argument and a get_dispatch_uid classmethod.
|
|
The signal argument must be the Django signal the class should be
|
|
connected to. The get_dispatch_uid classmethod must return a unique
|
|
value for each child class and None for base classes which will not be
|
|
connected.
|
|
|
|
The classmethod get_all_objects is added as get_all classmethod to every
|
|
class using this metaclass. Calling this on a base class or on child
|
|
classes will retrieve all connected children, on instance for each child
|
|
class.
|
|
|
|
These instances will have a check_permission method which returns True
|
|
by default. You can override this method to return False on runtime if
|
|
you want to filter some children.
|
|
|
|
They will also have a get_default_weight method which returns the value
|
|
of the default_weight attribute which is 0 by default. You can override
|
|
the attribute or the method to sort the children.
|
|
|
|
Example:
|
|
|
|
class Base(object):
|
|
__metaclass__ = SignalConnectMetaClass
|
|
signal = django.dispatch.Signal()
|
|
@classmethod
|
|
def get_dispatch_uid(cls):
|
|
if not cls.__name__ == 'Base':
|
|
return cls.__name__
|
|
|
|
class Child(Base):
|
|
def __init__(self, **kwargs):
|
|
pass
|
|
|
|
child = Base.get_all(request)[0]
|
|
assert Child == type(child)
|
|
"""
|
|
def __new__(metaclass, class_name, class_parents, class_attributes):
|
|
"""
|
|
Creates the class and connects it to the signal if so. Adds all
|
|
default attributes and methods.
|
|
"""
|
|
class_attributes['get_all'] = get_all_objects
|
|
new_class = super(SignalConnectMetaClass, metaclass).__new__(
|
|
metaclass, class_name, class_parents, class_attributes)
|
|
try:
|
|
dispatch_uid = new_class.get_dispatch_uid()
|
|
except AttributeError:
|
|
raise NotImplementedError('Your class %s must have a get_dispatch_uid classmethod.' % class_name)
|
|
if dispatch_uid is not None:
|
|
try:
|
|
signal = new_class.signal
|
|
except AttributeError:
|
|
raise NotImplementedError('Your class %s must have a signal argument, which must be a Django Signal instance.' % class_name)
|
|
else:
|
|
signal.connect(new_class, dispatch_uid=dispatch_uid)
|
|
attributes = {'check_permission': check_permission,
|
|
'get_default_weight': get_default_weight,
|
|
'default_weight': 0}
|
|
for name, attribute in attributes.items():
|
|
if not hasattr(new_class, name):
|
|
setattr(new_class, name, attribute)
|
|
return new_class
|
|
|
|
|
|
@classmethod
|
|
def get_all_objects(cls, request):
|
|
"""
|
|
Collects all objects of the class created by the SignalConnectMetaClass
|
|
from all apps via signal. They are sorted using the get_default_weight
|
|
method. Does not return objects where check_permission returns False.
|
|
|
|
Expects a request object.
|
|
|
|
This classmethod is added as get_all classmethod to every class using
|
|
the SignalConnectMetaClass.
|
|
"""
|
|
all_objects = [obj for __, obj in cls.signal.send(sender=cls, request=request) if obj.check_permission()]
|
|
all_objects.sort(key=lambda obj: obj.get_default_weight())
|
|
return all_objects
|
|
|
|
|
|
def check_permission(self):
|
|
"""
|
|
Returns True by default. Override this to filter some children on runtime.
|
|
|
|
This method is added to every instance of classes using the
|
|
SignalConnectMetaClass.
|
|
"""
|
|
return True
|
|
|
|
|
|
def get_default_weight(self):
|
|
"""
|
|
Returns the value of the default_weight attribute by default. Override
|
|
this to sort some children on runtime.
|
|
|
|
This method is added to every instance of classes using the
|
|
SignalConnectMetaClass.
|
|
"""
|
|
return self.default_weight
|