File: layout_slice.py

package info (click to toggle)
python-django-crispy-forms 1.7.2-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 1,248 kB
  • sloc: python: 3,907; makefile: 90
file content (160 lines) | stat: -rw-r--r-- 6,519 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# -*- coding: utf-8 -*-
from crispy_forms.bootstrap import Container
from crispy_forms.compatibility import integer_types, string_types
from crispy_forms.exceptions import DynamicError
from crispy_forms.layout import Fieldset, MultiField


class LayoutSlice(object):
    # List of layout objects that need args passed first before fields
    args_first = (Fieldset, MultiField, Container)

    def __init__(self, layout, key):
        self.layout = layout
        if isinstance(key, integer_types):
            self.slice = slice(key, key + 1, 1)
        else:
            self.slice = key

    def wrapped_object(self, LayoutClass, fields, *args, **kwargs):
        """
        Returns a layout object of type `LayoutClass` with `args` and `kwargs` that
        wraps `fields` inside.
        """
        if args:
            if isinstance(fields, list):
                fields = tuple(fields)
            else:
                fields = (fields,)

            if LayoutClass in self.args_first:
                arguments = args + fields
            else:
                arguments = fields + args

            return LayoutClass(*arguments, **kwargs)
        else:
            if isinstance(fields, list):
                return LayoutClass(*fields, **kwargs)
            else:
                return LayoutClass(fields, **kwargs)

    def pre_map(self, function):
        """
        Iterates over layout objects pointed in `self.slice` executing `function` on them.
        It passes `function` penultimate layout object and the position where to find last one
        """
        if isinstance(self.slice, slice):
            for i in range(*self.slice.indices(len(self.layout.fields))):
                function(self.layout, i)

        elif isinstance(self.slice, list):
            # A list of pointers  Ex: [[[0, 0], 'div'], [[0, 2, 3], 'field_name']]
            for pointer in self.slice:
                position = pointer[0]

                # If it's pointing first level
                if len(position) == 1:
                    function(self.layout, position[-1])
                else:
                    layout_object = self.layout.fields[position[0]]
                    for i in position[1:-1]:
                        layout_object = layout_object.fields[i]

                    try:
                        function(layout_object, position[-1])
                    except IndexError:
                        # We could avoid this exception, recalculating pointers.
                        # However this case is most of the time an undesired behavior
                        raise DynamicError("Trying to wrap a field within an already wrapped field, \
                            recheck your filter or layout")

    def wrap(self, LayoutClass, *args, **kwargs):
        """
        Wraps every layout object pointed in `self.slice` under a `LayoutClass` instance with
        `args` and `kwargs` passed.
        """
        def wrap_object(layout_object, j):
            layout_object.fields[j] = self.wrapped_object(
                LayoutClass, layout_object.fields[j], *args, **kwargs
            )

        self.pre_map(wrap_object)

    def wrap_once(self, LayoutClass, *args, **kwargs):
        """
        Wraps every layout object pointed in `self.slice` under a `LayoutClass` instance with
        `args` and `kwargs` passed, unless layout object's parent is already a subclass of
        `LayoutClass`.
        """
        def wrap_object_once(layout_object, j):
            if not isinstance(layout_object, LayoutClass):
                layout_object.fields[j] = self.wrapped_object(
                    LayoutClass, layout_object.fields[j], *args, **kwargs
                )

        self.pre_map(wrap_object_once)

    def wrap_together(self, LayoutClass, *args, **kwargs):
        """
        Wraps all layout objects pointed in `self.slice` together under a `LayoutClass`
        instance with `args` and `kwargs` passed.
        """
        if isinstance(self.slice, slice):
            # The start of the slice is replaced
            start = self.slice.start if self.slice.start is not None else 0
            self.layout.fields[start] = self.wrapped_object(
                LayoutClass, self.layout.fields[self.slice], *args, **kwargs
            )

            # The rest of places of the slice are removed, as they are included in the previous
            for i in reversed(range(*self.slice.indices(len(self.layout.fields)))):
                if i != start:
                    del self.layout.fields[i]

        elif isinstance(self.slice, list):
            raise DynamicError("wrap_together doesn't work with filter, only with [] operator")

    def map(self, function):
        """
        Iterates over layout objects pointed in `self.slice` executing `function` on them
        It passes `function` last layout object
        """
        if isinstance(self.slice, slice):
            for i in range(*self.slice.indices(len(self.layout.fields))):
                function(self.layout.fields[i])

        elif isinstance(self.slice, list):
            # A list of pointers  Ex: [[[0, 0], 'div'], [[0, 2, 3], 'field_name']]
            for pointer in self.slice:
                position = pointer[0]

                layout_object = self.layout.fields[position[0]]
                for i in position[1:]:
                    previous_layout_object = layout_object
                    layout_object = layout_object.fields[i]

                # If update_attrs is applied to a string, we call to its wrapping layout object
                if (
                    function.__name__ == 'update_attrs'
                    and isinstance(layout_object, string_types)
                ):
                    function(previous_layout_object)
                else:
                    function(layout_object)

    def update_attributes(self, **original_kwargs):
        """
        Updates attributes of every layout object pointed in `self.slice` using kwargs
        """
        def update_attrs(layout_object):
            kwargs = original_kwargs.copy()
            if hasattr(layout_object, 'attrs'):
                if 'css_class' in kwargs:
                    if 'class' in layout_object.attrs:
                        layout_object.attrs['class'] += " %s" % kwargs.pop('css_class')
                    else:
                        layout_object.attrs['class'] = kwargs.pop('css_class')
                layout_object.attrs.update(kwargs)

        self.map(update_attrs)