|
|
@@ -53,6 +53,8 @@ class CrudViewset(object):
|
|
53
|
53
|
instance_breadcrumb_text = u"{0}"
|
|
54
|
54
|
instance_breadcrumb_view_type = DETAIL_VIEW_TYPE
|
|
55
|
55
|
|
|
|
56
|
+ extra_instance_views = []
|
|
|
57
|
+
|
|
56
|
58
|
lookup_field = 'pk'
|
|
57
|
59
|
lookup_url_kwarg = 'pk'
|
|
58
|
60
|
lookup_url_kwarg_pattern = r'\d+'
|
|
|
@@ -140,6 +142,8 @@ class CrudViewset(object):
|
|
140
|
142
|
|
|
141
|
143
|
self.base_view_mixin = None
|
|
142
|
144
|
self.views = {}
|
|
|
145
|
+ self.instance_view_types = set(INSTANCE_VIEW_TYPES)
|
|
|
146
|
+ self.extra_instance_view_types = set()
|
|
143
|
147
|
|
|
144
|
148
|
self.exclude_views = set(self.exclude_views)
|
|
145
|
149
|
if self.is_list_view_nested:
|
|
|
@@ -173,6 +177,7 @@ class CrudViewset(object):
|
|
173
|
177
|
self.generate_detail_view()
|
|
174
|
178
|
self.generate_update_view()
|
|
175
|
179
|
self.generate_delete_view()
|
|
|
180
|
+ self.generate_extra_instance_views()
|
|
176
|
181
|
|
|
177
|
182
|
self.is_view_enabled = {view_type: view is not None for view_type, view in self.views.items()}
|
|
178
|
183
|
|
|
|
@@ -235,7 +240,7 @@ class CrudViewset(object):
|
|
235
|
240
|
|
|
236
|
241
|
def get_view_url_kwargs(self, view_type, view, instance=None):
|
|
237
|
242
|
kwargs = {name: view.kwargs[name] for name in self.view_url_kwarg_names}
|
|
238
|
|
- if view_type in INSTANCE_VIEW_TYPES:
|
|
|
243
|
+ if view_type in self.instance_view_types:
|
|
239
|
244
|
if isinstance(instance, dict):
|
|
240
|
245
|
kwargs[self.lookup_url_kwarg] = instance.get(self.lookup_field)
|
|
241
|
246
|
else:
|
|
|
@@ -275,7 +280,7 @@ class CrudViewset(object):
|
|
275
|
280
|
if not self.is_list_view_nested and self.is_view_enabled[LIST_VIEW_TYPE]:
|
|
276
|
281
|
breadcrumbs.append(self.get_list_view_breadcrumb(view, GET, instance))
|
|
277
|
282
|
|
|
278
|
|
- if view_type in INSTANCE_VIEW_TYPES:
|
|
|
283
|
+ if view_type in self.instance_view_types:
|
|
279
|
284
|
breadcrumbs += [{
|
|
280
|
285
|
'text': self.get_instance_breadcrumb_text(view, instance),
|
|
281
|
286
|
'url': self.get_view_url(self.instance_breadcrumb_view_type, view, GET, instance)
|
|
|
@@ -437,14 +442,13 @@ class CrudViewset(object):
|
|
437
|
442
|
class BaseViewMixin(object):
|
|
438
|
443
|
viewset = self
|
|
439
|
444
|
view_type = None
|
|
440
|
|
- final_breadcrumb = None
|
|
441
|
445
|
pk_url_kwarg = viewset.lookup_url_kwarg
|
|
442
|
446
|
|
|
443
|
447
|
def get_queryset(self):
|
|
444
|
448
|
return self.viewset.get_queryset(self)
|
|
445
|
449
|
|
|
446
|
|
- def get_view_title(self):
|
|
447
|
|
- raise NotImplementedError
|
|
|
450
|
+ # Derived classes must inherit or define
|
|
|
451
|
+ # def get_view_title(self)
|
|
448
|
452
|
|
|
449
|
453
|
def get_cached_object(self, viewset=None):
|
|
450
|
454
|
return self.cached_objects[self.viewset if viewset is None else viewset]
|
|
|
@@ -473,10 +477,11 @@ class CrudViewset(object):
|
|
473
|
477
|
viewset = self.viewset
|
|
474
|
478
|
|
|
475
|
479
|
breadcrumbs = viewset.get_breadcrumbs(self.view_type, self, self.request.GET)
|
|
476
|
|
- if self.final_breadcrumb is not None:
|
|
|
480
|
+ final_breadcrumb = getattr(self, 'final_breadcrumb', None)
|
|
|
481
|
+ if final_breadcrumb is not None:
|
|
477
|
482
|
breadcrumbs.append(
|
|
478
|
483
|
{
|
|
479
|
|
- 'text': self.final_breadcrumb,
|
|
|
484
|
+ 'text': final_breadcrumb,
|
|
480
|
485
|
# Includes the query string.
|
|
481
|
486
|
'url': self.request.get_full_path()
|
|
482
|
487
|
}
|
|
|
@@ -489,13 +494,20 @@ class CrudViewset(object):
|
|
489
|
494
|
context['is_view_enabled'] = viewset.is_view_enabled
|
|
490
|
495
|
context['actions'] = []
|
|
491
|
496
|
|
|
492
|
|
- if (self.view_type != LIST_VIEW_TYPE) and (viewset.is_list_view_nested or viewset.is_view_enabled[LIST_VIEW_TYPE]):
|
|
|
497
|
+ if (self.view_type in viewset.extra_instance_view_types):
|
|
|
498
|
+ back_url = viewset.get_view_url(self.viewset.instance_breadcrumb_view_type, self, self.request.GET, self.get_object())
|
|
|
499
|
+ elif ((self.view_type != LIST_VIEW_TYPE) and (viewset.is_list_view_nested or viewset.is_view_enabled[LIST_VIEW_TYPE])):
|
|
|
500
|
+ back_url = viewset.get_view_url(LIST_VIEW_TYPE, self, self.request.GET)
|
|
|
501
|
+ else:
|
|
|
502
|
+ back_url = None
|
|
|
503
|
+
|
|
|
504
|
+ if back_url is not None:
|
|
493
|
505
|
context['actions'].append(
|
|
494
|
506
|
[
|
|
495
|
507
|
{
|
|
496
|
508
|
'type': 'button',
|
|
497
|
509
|
'text': viewset.get_back_button_text(),
|
|
498
|
|
- 'url': viewset.get_view_url(LIST_VIEW_TYPE, self, self.request.GET),
|
|
|
510
|
+ 'url': back_url,
|
|
499
|
511
|
'button_class': viewset.get_back_button_class()
|
|
500
|
512
|
}
|
|
501
|
513
|
]
|
|
|
@@ -640,7 +652,7 @@ class CrudViewset(object):
|
|
640
|
652
|
{
|
|
641
|
653
|
'type': 'button',
|
|
642
|
654
|
'id': 'id_edit_button',
|
|
643
|
|
- 'url': viewset.get_view_url(UPDATE_VIEW_TYPE, self, self.request.GET, self.get_cached_object()),
|
|
|
655
|
+ 'url': viewset.get_view_url(UPDATE_VIEW_TYPE, self, self.request.GET, self.get_object()),
|
|
644
|
656
|
'text': edit_button_text,
|
|
645
|
657
|
'button_class': viewset.get_edit_button_class()
|
|
646
|
658
|
}
|
|
|
@@ -653,14 +665,16 @@ class CrudViewset(object):
|
|
653
|
665
|
{
|
|
654
|
666
|
'type': 'button',
|
|
655
|
667
|
'id': 'id_delete_button',
|
|
656
|
|
- 'url': viewset.get_view_url(DELETE_VIEW_TYPE, self, self.request.GET, self.get_cached_object()),
|
|
|
668
|
+ 'url': viewset.get_view_url(DELETE_VIEW_TYPE, self, self.request.GET, self.get_object()),
|
|
657
|
669
|
'text': delete_button_text,
|
|
658
|
670
|
'button_class': viewset.get_delete_button_class()
|
|
659
|
671
|
}
|
|
660
|
672
|
)
|
|
661
|
673
|
|
|
662
|
674
|
if action_group:
|
|
663
|
|
- context['actions'].append(action_group)
|
|
|
675
|
+ # HACK: Insert after the back button and before any other
|
|
|
676
|
+ # action groups.
|
|
|
677
|
+ context['actions'].insert(min(1, len(context['actions'])), action_group)
|
|
664
|
678
|
|
|
665
|
679
|
context['nested_list_views'] = []
|
|
666
|
680
|
if self.viewset.nested_list_views:
|
|
|
@@ -717,7 +731,7 @@ class CrudViewset(object):
|
|
717
|
731
|
[
|
|
718
|
732
|
{
|
|
719
|
733
|
'type': 'button',
|
|
720
|
|
- 'url': viewset.get_view_url(DELETE_VIEW_TYPE, self, self.request.GET, self.get_cached_object()),
|
|
|
734
|
+ 'url': viewset.get_view_url(DELETE_VIEW_TYPE, self, self.request.GET, self.get_object()),
|
|
721
|
735
|
'text': delete_button_text,
|
|
722
|
736
|
'button_class': viewset.get_delete_button_class()
|
|
723
|
737
|
}
|
|
|
@@ -775,6 +789,29 @@ class CrudViewset(object):
|
|
775
|
789
|
|
|
776
|
790
|
self.views[DELETE_VIEW_TYPE] = self.get_decorated_view(DeleteView)
|
|
777
|
791
|
|
|
|
792
|
+ def generate_extra_instance_views(self):
|
|
|
793
|
+ for instance_view in self.extra_instance_views:
|
|
|
794
|
+ my_view_type = instance_view['view_type']
|
|
|
795
|
+ if self.is_view_generated(my_view_type):
|
|
|
796
|
+ continue
|
|
|
797
|
+
|
|
|
798
|
+ class InstanceView(self.get_base_view_mixin(), instance_view['view_class']):
|
|
|
799
|
+ viewset = self
|
|
|
800
|
+ view_type = my_view_type
|
|
|
801
|
+
|
|
|
802
|
+ def dispatch(self, request, *args, **kwargs):
|
|
|
803
|
+ self.populate_object_cache(self.viewset.get_object(self))
|
|
|
804
|
+ return super(InstanceView, self).dispatch(request, *args, **kwargs)
|
|
|
805
|
+
|
|
|
806
|
+ def get_context_data(self, *args, **kwargs):
|
|
|
807
|
+ context = super(InstanceView, self).get_context_data(*args, **kwargs)
|
|
|
808
|
+ context['object'] = self.get_object()
|
|
|
809
|
+ return context
|
|
|
810
|
+
|
|
|
811
|
+ self.views[my_view_type] = self.get_decorated_view(InstanceView)
|
|
|
812
|
+ self.instance_view_types.add(my_view_type)
|
|
|
813
|
+ self.extra_instance_view_types.add(my_view_type)
|
|
|
814
|
+
|
|
778
|
815
|
@classmethod
|
|
779
|
816
|
def urls(cls):
|
|
780
|
817
|
viewset = cls()
|
|
|
@@ -794,6 +831,10 @@ class CrudViewset(object):
|
|
794
|
831
|
append_pattern(lookup_regex + 'update/$', UPDATE_VIEW_TYPE)
|
|
795
|
832
|
append_pattern(lookup_regex + 'delete/$', DELETE_VIEW_TYPE)
|
|
796
|
833
|
|
|
|
834
|
+ for instance_view in viewset.extra_instance_views:
|
|
|
835
|
+ view_type = instance_view['view_type']
|
|
|
836
|
+ patterns.append(url(lookup_regex + instance_view['regex'], viewset.views[view_type], name=view_type))
|
|
|
837
|
+
|
|
797
|
838
|
if not patterns:
|
|
798
|
839
|
# If a viewset functions only as a nested list view then it will
|
|
799
|
840
|
# not define any views of its own. We need at least one view URL
|