OpenSlides/openslides/utils/dispatch.py
Norman Jäckel 21ff62dd32 Changes in projector and core app and in utils.
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.
2014-01-10 16:17:54 +01:00

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