1
|
|
|
import re |
2
|
|
|
from math import floor |
3
|
|
|
from django import template |
4
|
|
|
from itertools import chain |
5
|
|
|
from django.template.defaultfilters import stringfilter |
6
|
|
|
from collections import namedtuple |
7
|
|
|
|
8
|
|
|
try: |
9
|
|
|
from django.utils.encoding import force_text |
10
|
|
|
except ImportError: |
11
|
|
|
from django.utils.encoding import force_unicode as force_text |
12
|
|
|
|
13
|
|
|
register = template.Library() |
14
|
|
|
Field = namedtuple('Field', 'name verbose_name') |
15
|
|
|
CrudDetail = namedtuple('CrudDetail', ['app', 'model', 'list_url']) |
16
|
|
|
|
17
|
|
|
|
18
|
|
|
@register.filter |
19
|
|
|
@stringfilter |
20
|
|
|
def undertospaced(value): |
21
|
|
|
return value.replace("_", " ").title() |
22
|
|
|
|
23
|
|
|
|
24
|
|
|
@register.filter |
25
|
|
|
def get_value(obj, field): |
26
|
|
|
try: |
27
|
|
|
return getattr(obj, 'get_%s_display' % field)() |
28
|
|
|
except: |
29
|
|
|
return getattr(obj, field) |
30
|
|
|
|
31
|
|
|
|
32
|
|
|
@register.filter |
33
|
|
|
def class_name(obj): |
34
|
|
|
return obj.__class__.__name__ |
35
|
|
|
|
36
|
|
|
|
37
|
|
|
@register.filter |
38
|
|
|
def crud_detail(crud_key): |
39
|
|
|
app, model, postfix_url = crud_key.split('-', 2) |
40
|
|
|
list_url = '{}-{}-list'.format(app, postfix_url) |
41
|
|
|
return CrudDetail(app, model, list_url) |
42
|
|
|
|
43
|
|
|
|
44
|
|
|
@register.filter |
45
|
|
|
def get_model_fields(obj, detail_exclude=None): |
46
|
|
|
model = obj.__class__ |
47
|
|
|
excludes = ['pk'] |
48
|
|
|
|
49
|
|
|
property_fields = [] |
50
|
|
|
for name in dir(model): |
51
|
|
|
if name not in excludes and isinstance( |
52
|
|
|
getattr(model, name, None), property |
53
|
|
|
): |
54
|
|
|
property_fields.append(Field(name=name, verbose_name=name)) |
55
|
|
|
fields = chain(obj._meta.fields, property_fields) |
56
|
|
|
|
57
|
|
|
if detail_exclude: |
58
|
|
|
fields = [field for field in fields if field.name not in detail_exclude] |
59
|
|
|
return fields |
60
|
|
|
|
61
|
|
|
|
62
|
|
|
@register.filter |
63
|
|
|
def get_verbose_field_name(instance, field_name): |
64
|
|
|
""" |
65
|
|
|
Returns verbose_name for a field. |
66
|
|
|
""" |
67
|
|
|
fields = [field.name for field in instance._meta.fields] |
68
|
|
|
if field_name in fields: |
69
|
|
|
return instance._meta.get_field(field_name).verbose_name |
70
|
|
|
else: |
71
|
|
|
return field_name |
72
|
|
|
|
73
|
|
|
|
74
|
|
|
@register.filter(is_safe=True) |
75
|
|
|
def label_with_class(value, arg): |
76
|
|
|
"""Style adjustments""" |
77
|
|
|
return value.label_tag(attrs={'class': arg}) |
78
|
|
|
|
79
|
|
|
|
80
|
|
|
@register.filter(is_safe=True) |
81
|
|
|
def input_with_class(value, arg): |
82
|
|
|
value.field.widget.attrs['class'] = arg |
83
|
|
|
return value |
84
|
|
|
|
85
|
|
|
|
86
|
|
|
@register.filter(is_safe=True) |
87
|
|
|
def inline_objects(object, inline_fk): |
88
|
|
|
inline_model = inline_fk.model |
89
|
|
|
related_filter = {inline_fk.name: object} |
90
|
|
|
return inline_model.objects.filter(**related_filter) |
91
|
|
|
|
92
|
|
|
|
93
|
|
|
@register.inclusion_tag('crudbuilder/widgets/tables/pagination.html') |
94
|
|
|
def bootstrap_pagination(page, **kwargs): |
95
|
|
|
pagination_kwargs = kwargs.copy() |
96
|
|
|
pagination_kwargs['page'] = page |
97
|
|
|
return get_pagination_context(**pagination_kwargs) |
98
|
|
|
|
99
|
|
|
|
100
|
|
|
def get_pagination_context(page, pages_to_show=11, |
101
|
|
|
url=None, size=None, extra=None, |
102
|
|
|
parameter_name='page'): |
103
|
|
|
""" |
104
|
|
|
Generate Bootstrap pagination context from a page object |
105
|
|
|
""" |
106
|
|
|
pages_to_show = int(pages_to_show) |
107
|
|
|
if pages_to_show < 1: |
108
|
|
|
raise ValueError( |
109
|
|
|
"Pagination pages_to_show should be a positive" |
110
|
|
|
"integer, you specified {pages}".format( |
111
|
|
|
pages=pages_to_show) |
112
|
|
|
) |
113
|
|
|
num_pages = page.paginator.num_pages |
114
|
|
|
current_page = page.number |
115
|
|
|
half_page_num = int(floor(pages_to_show / 2)) |
116
|
|
|
if half_page_num < 0: |
117
|
|
|
half_page_num = 0 |
118
|
|
|
first_page = current_page - half_page_num |
119
|
|
|
if first_page <= 1: |
120
|
|
|
first_page = 1 |
121
|
|
|
if first_page > 1: |
122
|
|
|
pages_back = first_page - half_page_num |
123
|
|
|
if pages_back < 1: |
124
|
|
|
pages_back = 1 |
125
|
|
|
else: |
126
|
|
|
pages_back = None |
127
|
|
|
last_page = first_page + pages_to_show - 1 |
128
|
|
|
if pages_back is None: |
129
|
|
|
last_page += 1 |
130
|
|
|
if last_page > num_pages: |
131
|
|
|
last_page = num_pages |
132
|
|
|
if last_page < num_pages: |
133
|
|
|
pages_forward = last_page + half_page_num |
134
|
|
|
if pages_forward > num_pages: |
135
|
|
|
pages_forward = num_pages |
136
|
|
|
else: |
137
|
|
|
pages_forward = None |
138
|
|
|
if first_page > 1: |
139
|
|
|
first_page -= 1 |
140
|
|
|
if pages_back is not None and pages_back > 1: |
141
|
|
|
pages_back -= 1 |
142
|
|
|
else: |
143
|
|
|
pages_back = None |
144
|
|
|
pages_shown = [] |
145
|
|
|
for i in range(first_page, last_page + 1): |
146
|
|
|
pages_shown.append(i) |
147
|
|
|
# Append proper character to url |
148
|
|
|
if url: |
149
|
|
|
# Remove existing page GET parameters |
150
|
|
|
url = force_text(url) |
151
|
|
|
url = re.sub(r'\?{0}\=[^\&]+'.format(parameter_name), '?', url) |
152
|
|
|
url = re.sub(r'\&{0}\=[^\&]+'.format(parameter_name), '', url) |
153
|
|
|
# Append proper separator |
154
|
|
|
if '?' in url: |
155
|
|
|
url += '&' |
156
|
|
|
else: |
157
|
|
|
url += '?' |
158
|
|
|
# Append extra string to url |
159
|
|
|
if extra: |
160
|
|
|
if not url: |
161
|
|
|
url = '?' |
162
|
|
|
url += force_text(extra) + '&' |
163
|
|
|
if url: |
164
|
|
|
url = url.replace('?&', '?') |
165
|
|
|
# Set CSS classes, see http://getbootstrap.com/components/#pagination |
166
|
|
|
pagination_css_classes = ['pagination'] |
167
|
|
|
if size == 'small': |
168
|
|
|
pagination_css_classes.append('pagination-sm') |
169
|
|
|
elif size == 'large': |
170
|
|
|
pagination_css_classes.append('pagination-lg') |
171
|
|
|
# Build context object |
172
|
|
|
return { |
173
|
|
|
'bootstrap_pagination_url': url, |
174
|
|
|
'num_pages': num_pages, |
175
|
|
|
'current_page': current_page, |
176
|
|
|
'first_page': first_page, |
177
|
|
|
'last_page': last_page, |
178
|
|
|
'pages_shown': pages_shown, |
179
|
|
|
'pages_back': pages_back, |
180
|
|
|
'pages_forward': pages_forward, |
181
|
|
|
'pagination_css_classes': ' '.join(pagination_css_classes), |
182
|
|
|
'parameter_name': parameter_name, |
183
|
|
|
} |
184
|
|
|
|