1
|
|
|
import operator |
2
|
|
|
import six |
3
|
|
|
from django.db.models import Q |
4
|
|
|
from django.http import HttpResponseRedirect |
5
|
|
|
from django.contrib.auth.views import redirect_to_login |
6
|
|
|
from django.contrib.auth import REDIRECT_FIELD_NAME |
7
|
|
|
from django.conf import settings |
8
|
|
|
from django.contrib import messages |
9
|
|
|
|
10
|
|
|
from crudbuilder.helpers import plural |
11
|
|
|
from crudbuilder.signals import crudbuilder_signals |
12
|
|
|
|
13
|
|
|
if six.PY3: |
14
|
|
|
from functools import reduce |
15
|
|
|
|
16
|
|
|
|
17
|
|
|
class LoginRequiredMixin(object): |
18
|
|
|
""" |
19
|
|
|
View mixin which verifies that login required for the crud builder |
20
|
|
|
""" |
21
|
|
|
login_url = settings.LOGIN_URL |
22
|
|
|
redirect_field_name = REDIRECT_FIELD_NAME # Set by django.contrib.auth |
23
|
|
|
|
24
|
|
|
def handle_login_required(self, request): |
25
|
|
|
return redirect_to_login( |
26
|
|
|
request.get_full_path(), |
27
|
|
|
self.login_url, |
28
|
|
|
self.redirect_field_name) |
29
|
|
|
|
30
|
|
|
def dispatch(self, request, *args, **kwargs): |
31
|
|
|
global_login_required = getattr( |
32
|
|
|
settings, 'LOGIN_REQUIRED_FOR_CRUD', False) |
33
|
|
|
|
34
|
|
|
if global_login_required or self.login_required: |
35
|
|
|
if not request.user.is_authenticated(): |
36
|
|
|
return self.handle_login_required(request) |
37
|
|
|
|
38
|
|
|
return super(LoginRequiredMixin, self).dispatch( |
39
|
|
|
request, *args, **kwargs) |
40
|
|
|
|
41
|
|
|
|
42
|
|
|
class PermissionRequiredMixin(object): |
43
|
|
|
""" |
44
|
|
|
View mixin which verifies that the logged in user has the specified |
45
|
|
|
permission. |
46
|
|
|
""" |
47
|
|
|
def check_permissions(self, request): |
48
|
|
|
perms = self.permissions |
49
|
|
|
result = request.user.has_perm(perms) if perms else True |
50
|
|
|
return result |
51
|
|
|
|
52
|
|
|
def dispatch(self, request, *args, **kwargs): |
53
|
|
|
has_permission = self.check_permissions(request) |
54
|
|
|
global_permission_required = getattr( |
55
|
|
|
settings, 'PERMISSION_REQUIRED_FOR_CRUD', False) |
56
|
|
|
|
57
|
|
|
if global_permission_required or self.permission_required: |
58
|
|
|
if not has_permission: |
59
|
|
|
return redirect_to_login( |
60
|
|
|
request.get_full_path(), |
61
|
|
|
settings.LOGIN_URL, |
62
|
|
|
REDIRECT_FIELD_NAME) |
63
|
|
|
|
64
|
|
|
return super(PermissionRequiredMixin, self).dispatch( |
65
|
|
|
request, *args, **kwargs) |
66
|
|
|
|
67
|
|
|
|
68
|
|
|
class CrudBuilderMixin(LoginRequiredMixin, PermissionRequiredMixin): |
69
|
|
|
""" |
70
|
|
|
- Mixin to provide additional context data for the templates |
71
|
|
|
- app_label : return app_label for CRUD model |
72
|
|
|
- actual_model_name : returns the actual model name (person -> person) |
73
|
|
|
- pluralized_model_name : return the pluralized_model_name |
74
|
|
|
(person -> people) |
75
|
|
|
""" |
76
|
|
|
def get_context_data(self, **kwargs): |
77
|
|
|
context = super(CrudBuilderMixin, self).get_context_data(**kwargs) |
78
|
|
|
model = context['view'].model |
79
|
|
|
context['app_label'] = model._meta.app_label |
80
|
|
|
context['actual_model_name'] = model.__name__.lower() |
81
|
|
|
context['pluralized_model_name'] = plural(model.__name__.lower()) |
82
|
|
|
context['verbose_model_name'] = model._meta.verbose_name |
83
|
|
|
context['verbose_model_name_plural'] = model._meta.verbose_name_plural |
84
|
|
|
context['project_name'] = getattr( |
85
|
|
|
settings, 'PROJECT_NAME', 'CRUDBUILDER') |
86
|
|
|
return context |
87
|
|
|
|
88
|
|
|
@property |
89
|
|
|
def get_actual_signal(self): |
90
|
|
|
view = 'update' if self.object else 'create' |
91
|
|
|
if self.inlineformset: |
92
|
|
|
return crudbuilder_signals['inlineformset'][view] |
93
|
|
|
else: |
94
|
|
|
return crudbuilder_signals['instance'][view] |
95
|
|
|
|
96
|
|
|
|
97
|
|
|
class BaseDetailViewMixin(CrudBuilderMixin): |
98
|
|
|
def get_context_data(self, **kwargs): |
99
|
|
|
context = super(BaseDetailViewMixin, self).get_context_data(**kwargs) |
100
|
|
|
context['inlineformset'] = self.inlineformset |
101
|
|
|
return context |
102
|
|
|
|
103
|
|
|
|
104
|
|
|
class CreateUpdateViewMixin(CrudBuilderMixin): |
105
|
|
|
"""Common form_valid() method for both Create and Update views""" |
106
|
|
|
def get_form_kwargs(self): |
107
|
|
|
kwargs = super(CreateUpdateViewMixin, self).get_form_kwargs() |
108
|
|
|
if self.custom_form: |
109
|
|
|
kwargs.update({'request': self.request}) |
110
|
|
|
return kwargs |
111
|
|
|
|
112
|
|
|
def form_valid(self, form): |
113
|
|
|
signal = self.get_actual_signal |
114
|
|
|
instance = form.save(commit=False) |
115
|
|
|
signal.send(sender=self.model, request=self.request, instance=instance) |
116
|
|
|
instance.save() |
117
|
|
|
form.save_m2m() |
118
|
|
|
return super(CreateUpdateViewMixin, self).form_valid(form) |
119
|
|
|
|
120
|
|
|
|
121
|
|
|
class BaseListViewMixin(CrudBuilderMixin): |
122
|
|
|
""" |
123
|
|
|
- Search implementation for tables2 in ListView. |
124
|
|
|
- search_fields will be defined in actual model |
125
|
|
|
- Override get_queryset method of ListView |
126
|
|
|
""" |
127
|
|
|
def get_queryset(self): |
128
|
|
|
if self.custom_queryset: |
129
|
|
|
objects = self.custom_queryset(self.request, **self.kwargs) |
130
|
|
|
else: |
131
|
|
|
objects = self.model.objects.all() |
132
|
|
|
search = self.request.GET.get('search') |
133
|
|
|
if search: |
134
|
|
|
q_list = [ |
135
|
|
|
Q( |
136
|
|
|
('{}__icontains'.format(field), search)) |
137
|
|
|
for field in self.crud.search_fields |
138
|
|
|
] |
139
|
|
|
objects = objects.filter(reduce(operator.or_, q_list)) |
140
|
|
|
return objects.order_by('-id') |
141
|
|
|
|
142
|
|
|
def get_context_data(self, **kwargs): |
143
|
|
|
context = super(BaseListViewMixin, self).get_context_data(**kwargs) |
144
|
|
|
if self.custom_context: |
145
|
|
|
custom_context = self.custom_context(self.request, context, **kwargs) |
146
|
|
|
context.update(custom_context) |
147
|
|
|
return context |
148
|
|
|
|
149
|
|
|
|
150
|
|
|
class InlineFormsetViewMixin(CrudBuilderMixin): |
151
|
|
|
def get_context_data(self, **kwargs): |
152
|
|
|
context = super( |
153
|
|
|
InlineFormsetViewMixin, self).get_context_data(**kwargs) |
154
|
|
|
if self.request.POST: |
155
|
|
|
context['inlineformset'] = self.inlineformset( |
156
|
|
|
self.request.POST, |
157
|
|
|
instance=self.object |
158
|
|
|
) |
159
|
|
|
else: |
160
|
|
|
context['inlineformset'] = self.inlineformset( |
161
|
|
|
instance=self.object |
162
|
|
|
) |
163
|
|
|
return context |
164
|
|
|
|
165
|
|
|
def form_valid(self, form): |
166
|
|
|
context = self.get_context_data() |
167
|
|
|
inlineformset = context['inlineformset'] |
168
|
|
|
|
169
|
|
|
if inlineformset.is_valid(): |
170
|
|
|
parent = form.save(commit=False) |
171
|
|
|
inlineformset.instance = parent |
172
|
|
|
children = inlineformset.save(commit=False) |
173
|
|
|
|
174
|
|
|
# execute post signals |
175
|
|
|
signal = self.get_actual_signal |
176
|
|
|
signal.send( |
177
|
|
|
sender=self.model, |
178
|
|
|
request=self.request, |
179
|
|
|
parent=parent, |
180
|
|
|
children=children) |
181
|
|
|
|
182
|
|
|
parent.save() |
183
|
|
|
inlineformset.save() |
184
|
|
|
inlineformset.save_m2m() |
185
|
|
|
|
186
|
|
|
return HttpResponseRedirect(self.success_url) |
187
|
|
|
else: |
188
|
|
|
messages.error( |
189
|
|
|
self.request, inlineformset.non_form_errors()) |
190
|
|
|
return self.render_to_response( |
191
|
|
|
self.get_context_data(form=form, inlineformset=inlineformset) |
192
|
|
|
) |
193
|
|
|
|