3
3
from django .core .exceptions import ObjectDoesNotExist
4
4
from django .db .models import F , Q
5
5
from django .http import Http404
6
+ from django .shortcuts import get_list_or_404
6
7
from django .urls .base import reverse
7
8
from django_filters .rest_framework import DjangoFilterBackend
8
9
from rest_framework import pagination , serializers , status
19
20
20
21
from openwisp_users .api .permissions import DjangoModelPermissions
21
22
22
- from ...mixins import ProtectedAPIMixin
23
+ from ...mixins import AutoRevisionMixin , ProtectedAPIMixin
23
24
from .filters import (
24
25
DeviceGroupListFilter ,
25
26
DeviceListFilter ,
26
27
DeviceListFilterBackend ,
27
- ReversionFilter ,
28
28
TemplateListFilter ,
29
29
VPNListFilter ,
30
30
)
@@ -53,28 +53,30 @@ class ListViewPagination(pagination.PageNumberPagination):
53
53
max_page_size = 100
54
54
55
55
56
- class TemplateListCreateView (ProtectedAPIMixin , ListCreateAPIView ):
56
+ class TemplateListCreateView (ProtectedAPIMixin , AutoRevisionMixin , ListCreateAPIView ):
57
57
serializer_class = TemplateSerializer
58
58
queryset = Template .objects .prefetch_related ("tags" ).order_by ("-created" )
59
59
pagination_class = ListViewPagination
60
60
filter_backends = [DjangoFilterBackend ]
61
61
filterset_class = TemplateListFilter
62
62
63
63
64
- class TemplateDetailView (ProtectedAPIMixin , RetrieveUpdateDestroyAPIView ):
64
+ class TemplateDetailView (
65
+ ProtectedAPIMixin , AutoRevisionMixin , RetrieveUpdateDestroyAPIView
66
+ ):
65
67
serializer_class = TemplateSerializer
66
68
queryset = Template .objects .all ()
67
69
68
70
69
- class VpnListCreateView (ProtectedAPIMixin , ListCreateAPIView ):
71
+ class VpnListCreateView (ProtectedAPIMixin , AutoRevisionMixin , ListCreateAPIView ):
70
72
serializer_class = VpnSerializer
71
73
queryset = Vpn .objects .select_related ("subnet" ).order_by ("-created" )
72
74
pagination_class = ListViewPagination
73
75
filter_backends = [DjangoFilterBackend ]
74
76
filterset_class = VPNListFilter
75
77
76
78
77
- class VpnDetailView (ProtectedAPIMixin , RetrieveUpdateDestroyAPIView ):
79
+ class VpnDetailView (ProtectedAPIMixin , AutoRevisionMixin , RetrieveUpdateDestroyAPIView ):
78
80
serializer_class = VpnSerializer
79
81
queryset = Vpn .objects .all ()
80
82
@@ -87,7 +89,7 @@ def has_object_permission(self, request, view, obj):
87
89
return perm and not obj .is_deactivated ()
88
90
89
91
90
- class DeviceListCreateView (ProtectedAPIMixin , ListCreateAPIView ):
92
+ class DeviceListCreateView (ProtectedAPIMixin , AutoRevisionMixin , ListCreateAPIView ):
91
93
"""
92
94
Templates: Templates flagged as required will be added automatically
93
95
to the `config` of a device and cannot be unassigned.
@@ -102,7 +104,9 @@ class DeviceListCreateView(ProtectedAPIMixin, ListCreateAPIView):
102
104
filterset_class = DeviceListFilter
103
105
104
106
105
- class DeviceDetailView (ProtectedAPIMixin , RetrieveUpdateDestroyAPIView ):
107
+ class DeviceDetailView (
108
+ ProtectedAPIMixin , AutoRevisionMixin , RetrieveUpdateDestroyAPIView
109
+ ):
106
110
"""
107
111
Templates: Templates flagged as _required_ will be added automatically
108
112
to the `config` of a device and cannot be unassigned.
@@ -129,7 +133,7 @@ def get_serializer_context(self):
129
133
return context
130
134
131
135
132
- class DeviceActivateView (ProtectedAPIMixin , GenericAPIView ):
136
+ class DeviceActivateView (ProtectedAPIMixin , AutoRevisionMixin , GenericAPIView ):
133
137
serializer_class = serializers .Serializer
134
138
queryset = Device .objects .filter (_is_deactivated = True )
135
139
@@ -142,7 +146,7 @@ def post(self, request, *args, **kwargs):
142
146
return Response (serializer .data , status = status .HTTP_200_OK )
143
147
144
148
145
- class DeviceDeactivateView (ProtectedAPIMixin , GenericAPIView ):
149
+ class DeviceDeactivateView (ProtectedAPIMixin , AutoRevisionMixin , GenericAPIView ):
146
150
serializer_class = serializers .Serializer
147
151
queryset = Device .objects .filter (_is_deactivated = False )
148
152
@@ -155,15 +159,19 @@ def post(self, request, *args, **kwargs):
155
159
return Response (serializer .data , status = status .HTTP_200_OK )
156
160
157
161
158
- class DeviceGroupListCreateView (ProtectedAPIMixin , ListCreateAPIView ):
162
+ class DeviceGroupListCreateView (
163
+ ProtectedAPIMixin , AutoRevisionMixin , ListCreateAPIView
164
+ ):
159
165
serializer_class = DeviceGroupSerializer
160
166
queryset = DeviceGroup .objects .prefetch_related ("templates" ).order_by ("-created" )
161
167
pagination_class = ListViewPagination
162
168
filter_backends = [DjangoFilterBackend ]
163
169
filterset_class = DeviceGroupListFilter
164
170
165
171
166
- class DeviceGroupDetailView (ProtectedAPIMixin , RetrieveUpdateDestroyAPIView ):
172
+ class DeviceGroupDetailView (
173
+ ProtectedAPIMixin , AutoRevisionMixin , RetrieveUpdateDestroyAPIView
174
+ ):
167
175
serializer_class = DeviceGroupSerializer
168
176
queryset = DeviceGroup .objects .select_related ("organization" ).order_by ("-created" )
169
177
@@ -177,7 +185,7 @@ def get_cached_devicegroup_args_rewrite(cls, org_slugs, common_name):
177
185
return url
178
186
179
187
180
- class DeviceGroupCommonName (ProtectedAPIMixin , RetrieveAPIView ):
188
+ class DeviceGroupCommonName (ProtectedAPIMixin , AutoRevisionMixin , RetrieveAPIView ):
181
189
serializer_class = DeviceGroupSerializer
182
190
queryset = DeviceGroup .objects .select_related ("organization" ).order_by ("-created" )
183
191
# Not setting lookup_field makes DRF raise error. but it is not used
@@ -294,39 +302,55 @@ def certificate_delete_invalidates_cache(cls, organization_id, common_name):
294
302
cls .get_device_group .invalidate (cls , org_slug , common_name )
295
303
296
304
297
- class ReversionListView (ProtectedAPIMixin , ListAPIView ):
305
+ class RevisionListView (ProtectedAPIMixin , AutoRevisionMixin , ListAPIView ):
298
306
serializer_class = ReversionSerializer
299
- queryset = Version .objects .select_related ('revision' ).order_by (
300
- '-revision__date_created'
301
- )
302
- filter_backends = [DjangoFilterBackend ]
303
- filterset_class = ReversionFilter
307
+
308
+ def get_queryset (self ):
309
+ model_slug = self .kwargs .get ('model_slug' ).lower ()
310
+ return (
311
+ Version .objects .select_related ('revision' )
312
+ .filter (content_type__model = model_slug )
313
+ .order_by ('-revision__date_created' )
314
+ )
304
315
305
316
306
- class ReversionDetailView (ProtectedAPIMixin , RetrieveAPIView ):
317
+ class RevisionDetailView (ProtectedAPIMixin , RetrieveAPIView ):
307
318
serializer_class = ReversionSerializer
308
- queryset = Version .objects .select_related ('revision' ).order_by (
309
- '-revision__date_created'
310
- )
311
- lookup_field = 'pk'
312
319
320
+ def get_queryset (self ):
321
+ model_slug = self .kwargs .get ('model_slug' ).lower ()
322
+ return (
323
+ Version .objects .select_related ('revision' )
324
+ .filter (content_type__model = model_slug )
325
+ .order_by ('-revision__date_created' )
326
+ )
313
327
314
- class ReversionRestoreView (ProtectedAPIMixin , GenericAPIView ):
328
+
329
+ class RevisionRestoreView (ProtectedAPIMixin , GenericAPIView ):
315
330
serializer_class = serializers .Serializer
316
- queryset = Version .objects .select_related ('revision' ).order_by (
317
- '-revision__date_created'
318
- )
331
+
332
+ def get_queryset (self ):
333
+ model_slug = self .kwargs .get ('model_slug' ).lower ()
334
+ return (
335
+ Version .objects .select_related ('revision' )
336
+ .filter (content_type__model = model_slug )
337
+ .order_by ('-revision__date_created' )
338
+ )
319
339
320
340
def post (self , request , * args , ** kwargs ):
321
- version = self .get_object ()
341
+ qs = self .get_queryset ()
342
+ versions = get_list_or_404 (qs , revision_id = kwargs ['pk' ])
322
343
with reversion .create_revision ():
323
- version .revert ()
344
+ for version in versions :
345
+ version .revert ()
324
346
reversion .set_user (request .user )
325
347
reversion .set_comment (
326
- f"Restored to previous revision: { version . revision_id } "
348
+ f"Restored to previous revision: { self . kwargs . get ( 'pk' ) } "
327
349
)
328
350
329
- serializer = ReversionSerializer (version , context = self .get_serializer_context ())
351
+ serializer = ReversionSerializer (
352
+ versions , many = True , context = self .get_serializer_context ()
353
+ )
330
354
return Response (serializer .data , status = status .HTTP_200_OK )
331
355
332
356
@@ -341,6 +365,6 @@ def post(self, request, *args, **kwargs):
341
365
devicegroup_list = DeviceGroupListCreateView .as_view ()
342
366
devicegroup_detail = DeviceGroupDetailView .as_view ()
343
367
devicegroup_commonname = DeviceGroupCommonName .as_view ()
344
- reversion_list = ReversionListView .as_view ()
345
- reversion_detail = ReversionDetailView .as_view ()
346
- reversion_restore = ReversionRestoreView .as_view ()
368
+ revision_list = RevisionListView .as_view ()
369
+ revision_detail = RevisionDetailView .as_view ()
370
+ revision_restore = RevisionRestoreView .as_view ()
0 commit comments