setup_generic_relations()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 3
c 3
b 1
f 0
dl 0
loc 32
rs 9.112
1
from inspect import isclass
2
3
import django
4
from django.apps import apps
5
from django.contrib.contenttypes.fields import GenericRelation
6
from django.db.models.base import ModelBase
7
from django.core.exceptions import ImproperlyConfigured
8
from django.utils.six import string_types
9
10
11
class RegistrationError(Exception):
12
    pass
13
14
15
def setup_generic_relations(model_class):
16
    """
17
    Set up GenericRelations for actionable models.
18
    """
19
    Action = apps.get_model('actstream', 'action')
20
21
    if Action is None:
22
        raise RegistrationError(
23
            'Unable get actstream.Action. Potential circular imports '
24
            'in initialisation. Try moving actstream app to come after the '
25
            'apps which have models to register in the INSTALLED_APPS setting.'
26
        )
27
28
    related_attr_name = 'related_query_name'
29
    related_attr_value = 'actions_with_%s' % label(model_class)
30
31
    relations = {}
32
    for field in ('actor', 'target', 'action_object'):
33
        attr = '%s_actions' % field
34
        attr_value = '%s_as_%s' % (related_attr_value, field)
35
        kwargs = {
36
            'content_type_field': '%s_content_type' % field,
37
            'object_id_field': '%s_object_id' % field,
38
            related_attr_name: attr_value
39
        }
40
        rel = GenericRelation('actstream.Action', **kwargs)
41
        rel.contribute_to_class(model_class, attr)
42
        relations[field] = rel
43
44
        # @@@ I'm not entirely sure why this works
45
        setattr(Action, attr_value, None)
46
    return relations
47
48
49
def label(model_class):
50
    if hasattr(model_class._meta, 'model_name'):
51
        model_name = model_class._meta.model_name
52
    else:
53
        model_name = model_class._meta.module_name
54
    return '%s_%s' % (model_class._meta.app_label, model_name)
55
56
57
def is_installed(model_class):
58
    """
59
    Returns True if a model_class is installed.
60
    model_class._meta.installed is only reliable in Django 1.7+
61
    """
62
    return model_class._meta.installed
63
64
65
def validate(model_class, exception_class=ImproperlyConfigured):
66
    if isinstance(model_class, string_types):
67
        model_class = apps.get_model(*model_class.split('.'))
68
    if not isinstance(model_class, ModelBase):
69
        raise exception_class(
70
            'Object %r is not a Model class.' % model_class)
71
    if model_class._meta.abstract:
72
        raise exception_class(
73
            'The model %r is abstract, so it cannot be registered with '
74
            'actstream.' % model_class)
75
    if not is_installed(model_class):
76
        raise exception_class(
77
            'The model %r is not installed, please put the app "%s" in your '
78
            'INSTALLED_APPS setting.' % (model_class,
79
                                         model_class._meta.app_label))
80
    return model_class
81
82
83
class ActionableModelRegistry(dict):
84
85
    def register(self, *model_classes_or_labels):
86
        for class_or_label in model_classes_or_labels:
87
            model_class = validate(class_or_label)
88
            if model_class not in self:
89
                self[model_class] = setup_generic_relations(model_class)
90
91
    def unregister(self, *model_classes_or_labels):
92
        for class_or_label in model_classes_or_labels:
93
            model_class = validate(class_or_label)
94
            if model_class in self:
95
                del self[model_class]
96
97
    def check(self, model_class_or_object):
98
        if getattr(model_class_or_object, '_deferred', None):
99
            model_class_or_object = model_class_or_object._meta.proxy_for_model
100
        if not isclass(model_class_or_object):
101
            model_class_or_object = model_class_or_object.__class__
102
        model_class = validate(model_class_or_object, RuntimeError)
103
        if model_class not in self:
104
            raise ImproperlyConfigured(
105
                'The model %s is not registered. Please use actstream.registry '
106
                'to register it.' % model_class.__name__)
107
108
109
registry = ActionableModelRegistry()
110
register = registry.register
111
unregister = registry.unregister
112
check = registry.check
113