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.
@@ -117,7 +121,7 @@ def perform_destroy(self, instance):
117
121
instance .delete (check_deactivated = (not force_deletion ))
118
122
119
123
120
- class DeviceActivateView (ProtectedAPIMixin , GenericAPIView ):
124
+ class DeviceActivateView (ProtectedAPIMixin , AutoRevisionMixin , GenericAPIView ):
121
125
serializer_class = serializers .Serializer
122
126
queryset = Device .objects .filter (_is_deactivated = True )
123
127
@@ -130,7 +134,7 @@ def post(self, request, *args, **kwargs):
130
134
return Response (serializer .data , status = status .HTTP_200_OK )
131
135
132
136
133
- class DeviceDeactivateView (ProtectedAPIMixin , GenericAPIView ):
137
+ class DeviceDeactivateView (ProtectedAPIMixin , AutoRevisionMixin , GenericAPIView ):
134
138
serializer_class = serializers .Serializer
135
139
queryset = Device .objects .filter (_is_deactivated = False )
136
140
@@ -143,15 +147,19 @@ def post(self, request, *args, **kwargs):
143
147
return Response (serializer .data , status = status .HTTP_200_OK )
144
148
145
149
146
- class DeviceGroupListCreateView (ProtectedAPIMixin , ListCreateAPIView ):
150
+ class DeviceGroupListCreateView (
151
+ ProtectedAPIMixin , AutoRevisionMixin , ListCreateAPIView
152
+ ):
147
153
serializer_class = DeviceGroupSerializer
148
154
queryset = DeviceGroup .objects .prefetch_related ('templates' ).order_by ('-created' )
149
155
pagination_class = ListViewPagination
150
156
filter_backends = [DjangoFilterBackend ]
151
157
filterset_class = DeviceGroupListFilter
152
158
153
159
154
- class DeviceGroupDetailView (ProtectedAPIMixin , RetrieveUpdateDestroyAPIView ):
160
+ class DeviceGroupDetailView (
161
+ ProtectedAPIMixin , AutoRevisionMixin , RetrieveUpdateDestroyAPIView
162
+ ):
155
163
serializer_class = DeviceGroupSerializer
156
164
queryset = DeviceGroup .objects .select_related ('organization' ).order_by ('-created' )
157
165
@@ -165,7 +173,7 @@ def get_cached_devicegroup_args_rewrite(cls, org_slugs, common_name):
165
173
return url
166
174
167
175
168
- class DeviceGroupCommonName (ProtectedAPIMixin , RetrieveAPIView ):
176
+ class DeviceGroupCommonName (ProtectedAPIMixin , AutoRevisionMixin , RetrieveAPIView ):
169
177
serializer_class = DeviceGroupSerializer
170
178
queryset = DeviceGroup .objects .select_related ('organization' ).order_by ('-created' )
171
179
# Not setting lookup_field makes DRF raise error. but it is not used
@@ -282,39 +290,55 @@ def certificate_delete_invalidates_cache(cls, organization_id, common_name):
282
290
cls .get_device_group .invalidate (cls , org_slug , common_name )
283
291
284
292
285
- class ReversionListView (ProtectedAPIMixin , ListAPIView ):
293
+ class RevisionListView (ProtectedAPIMixin , AutoRevisionMixin , ListAPIView ):
286
294
serializer_class = ReversionSerializer
287
- queryset = Version .objects .select_related ('revision' ).order_by (
288
- '-revision__date_created'
289
- )
290
- filter_backends = [DjangoFilterBackend ]
291
- filterset_class = ReversionFilter
295
+
296
+ def get_queryset (self ):
297
+ model_slug = self .kwargs .get ('model_slug' ).lower ()
298
+ return (
299
+ Version .objects .select_related ('revision' )
300
+ .filter (content_type__model = model_slug )
301
+ .order_by ('-revision__date_created' )
302
+ )
292
303
293
304
294
- class ReversionDetailView (ProtectedAPIMixin , RetrieveAPIView ):
305
+ class RevisionDetailView (ProtectedAPIMixin , RetrieveAPIView ):
295
306
serializer_class = ReversionSerializer
296
- queryset = Version .objects .select_related ('revision' ).order_by (
297
- '-revision__date_created'
298
- )
299
- lookup_field = 'pk'
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
+ )
300
315
301
316
302
- class ReversionRestoreView (ProtectedAPIMixin , GenericAPIView ):
317
+ class RevisionRestoreView (ProtectedAPIMixin , GenericAPIView ):
303
318
serializer_class = serializers .Serializer
304
- queryset = Version .objects .select_related ('revision' ).order_by (
305
- '-revision__date_created'
306
- )
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
+ )
307
327
308
328
def post (self , request , * args , ** kwargs ):
309
- version = self .get_object ()
329
+ qs = self .get_queryset ()
330
+ versions = get_list_or_404 (qs , revision_id = kwargs ['pk' ])
310
331
with reversion .create_revision ():
311
- version .revert ()
332
+ for version in versions :
333
+ version .revert ()
312
334
reversion .set_user (request .user )
313
335
reversion .set_comment (
314
- f"Restored to previous revision: { version . revision_id } "
336
+ f"Restored to previous revision: { self . kwargs . get ( 'pk' ) } "
315
337
)
316
338
317
- serializer = ReversionSerializer (version , context = self .get_serializer_context ())
339
+ serializer = ReversionSerializer (
340
+ versions , many = True , context = self .get_serializer_context ()
341
+ )
318
342
return Response (serializer .data , status = status .HTTP_200_OK )
319
343
320
344
@@ -329,6 +353,6 @@ def post(self, request, *args, **kwargs):
329
353
devicegroup_list = DeviceGroupListCreateView .as_view ()
330
354
devicegroup_detail = DeviceGroupDetailView .as_view ()
331
355
devicegroup_commonname = DeviceGroupCommonName .as_view ()
332
- reversion_list = ReversionListView .as_view ()
333
- reversion_detail = ReversionDetailView .as_view ()
334
- reversion_restore = ReversionRestoreView .as_view ()
356
+ revision_list = RevisionListView .as_view ()
357
+ revision_detail = RevisionDetailView .as_view ()
358
+ revision_restore = RevisionRestoreView .as_view ()
0 commit comments