views.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. from django.conf import settings
  2. from django.contrib import messages
  3. from django.contrib.admin.views.decorators import staff_member_required
  4. from django.contrib.contenttypes.models import ContentType
  5. from django.core.urlresolvers import reverse
  6. from django.db import transaction, IntegrityError
  7. from django.db.models import ProtectedError
  8. from django.http import HttpResponseRedirect
  9. from django.shortcuts import get_object_or_404, redirect, render
  10. from django.template import TemplateSyntaxError
  11. from django.utils.decorators import method_decorator
  12. from django.views.generic import View
  13. from django_tables2 import RequestConfig
  14. from .error_handlers import handle_protectederror
  15. from .paginator import EnhancedPaginator
  16. from extras.models import ExportTemplate
  17. class ObjectListView(View):
  18. queryset = None
  19. filter = None
  20. filter_form = None
  21. table = None
  22. edit_table = None
  23. edit_table_permissions = []
  24. template_name = None
  25. redirect_on_single_result = True
  26. def get(self, request, *args, **kwargs):
  27. object_ct = ContentType.objects.get_for_model(self.queryset.model)
  28. if self.filter:
  29. self.queryset = self.filter(request.GET, self.queryset).qs
  30. # Check for export template rendering
  31. if request.GET.get('export'):
  32. et = get_object_or_404(ExportTemplate, content_type=object_ct, name=request.GET.get('export'))
  33. try:
  34. response = et.to_response(context_dict={'queryset': self.queryset},
  35. filename='netbox_{}'.format(self.queryset.model._meta.verbose_name_plural))
  36. return response
  37. except TemplateSyntaxError:
  38. messages.error(request, "There was an error rendering the selected export template ({}).".format(et.name))
  39. # Attempt to redirect automatically if the query returns a single result
  40. if self.redirect_on_single_result and self.queryset.count() == 1:
  41. try:
  42. return HttpResponseRedirect(self.queryset[0].get_absolute_url())
  43. except AttributeError:
  44. pass
  45. # Construct the table based on the user's permissions
  46. if any([request.user.has_perm(perm) for perm in self.edit_table_permissions]):
  47. table = self.edit_table(self.queryset)
  48. else:
  49. table = self.table(self.queryset)
  50. RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator})\
  51. .configure(table)
  52. export_templates = ExportTemplate.objects.filter(content_type=object_ct)
  53. return render(request, self.template_name, {
  54. 'table': table,
  55. 'filter_form': self.filter_form(request.GET, label_suffix='') if self.filter_form else None,
  56. 'export_templates': export_templates,
  57. })
  58. class BulkImportView(View):
  59. form = None
  60. table = None
  61. template_name = None
  62. obj_list_url = None
  63. def get(self, request, *args, **kwargs):
  64. return render(request, self.template_name, {
  65. 'form': self.form(),
  66. 'obj_list_url': self.obj_list_url,
  67. })
  68. def post(self, request, *args, **kwargs):
  69. form = self.form(request.POST)
  70. if form.is_valid():
  71. new_objs = []
  72. try:
  73. with transaction.atomic():
  74. for obj in form.cleaned_data['csv']:
  75. self.save_obj(obj)
  76. new_objs.append(obj)
  77. obj_table = self.table(new_objs)
  78. messages.success(request, "Imported {} objects".format(len(new_objs)))
  79. return render(request, "import_success.html", {
  80. 'table': obj_table,
  81. })
  82. except IntegrityError as e:
  83. form.add_error('csv', "Record {}: {}".format(len(new_objs) + 1, e.__cause__))
  84. return render(request, self.template_name, {
  85. 'form': form,
  86. 'obj_list_url': self.obj_list_url,
  87. })
  88. def save_obj(self, obj):
  89. obj.save()
  90. class BulkEditView(View):
  91. cls = None
  92. form = None
  93. template_name = None
  94. redirect_url = None
  95. def get(self, request, *args, **kwargs):
  96. return redirect(self.redirect_url)
  97. def post(self, request, *args, **kwargs):
  98. if '_apply' in request.POST:
  99. form = self.form(request.POST)
  100. if form.is_valid():
  101. pk_list = [obj.pk for obj in form.cleaned_data['pk']]
  102. self.update_objects(pk_list, form)
  103. if not form.errors:
  104. return redirect(self.redirect_url)
  105. else:
  106. form = self.form(initial={'pk': request.POST.getlist('pk')})
  107. selected_objects = self.cls.objects.filter(pk__in=request.POST.getlist('pk'))
  108. if not selected_objects:
  109. messages.warning(request, "No {} were selected.".format(self.cls._meta.verbose_name_plural))
  110. return redirect(self.redirect_url)
  111. return render(request, self.template_name, {
  112. 'form': form,
  113. 'selected_objects': selected_objects,
  114. 'cancel_url': reverse(self.redirect_url),
  115. })
  116. def update_objects(self, obj_list, form):
  117. """
  118. This method provides the update logic (must be overridden by subclasses).
  119. """
  120. raise NotImplementedError()
  121. class BulkDeleteView(View):
  122. cls = None
  123. form = None
  124. template_name = None
  125. redirect_url = None
  126. @method_decorator(staff_member_required)
  127. def dispatch(self, *args, **kwargs):
  128. return super(BulkDeleteView, self).dispatch(*args, **kwargs)
  129. def get(self, request, *args, **kwargs):
  130. return redirect(self.redirect_url)
  131. def post(self, request, *args, **kwargs):
  132. if '_confirm' in request.POST:
  133. form = self.form(request.POST)
  134. if form.is_valid():
  135. # Delete objects
  136. objects_to_delete = self.cls.objects.filter(pk__in=[v.id for v in form.cleaned_data['pk']])
  137. try:
  138. deleted_count = objects_to_delete.count()
  139. objects_to_delete.delete()
  140. except ProtectedError, e:
  141. handle_protectederror(list(objects_to_delete), request, e)
  142. return redirect(self.redirect_url)
  143. messages.success(request, "Deleted {} {}".format(deleted_count, self.cls._meta.verbose_name_plural))
  144. return redirect(self.redirect_url)
  145. else:
  146. form = self.form(initial={'pk': request.POST.getlist('pk')})
  147. selected_objects = self.cls.objects.filter(pk__in=form.initial.get('pk'))
  148. if not selected_objects:
  149. messages.warning(request, "No {} were selected for deletion.".format(self.cls._meta.verbose_name_plural))
  150. return redirect(self.redirect_url)
  151. return render(request, self.template_name, {
  152. 'form': form,
  153. 'selected_objects': selected_objects,
  154. 'cancel_url': reverse(self.redirect_url),
  155. })