Merged v2.5.13

This commit is contained in:
Jeremy Stretch
2019-05-31 21:37:41 -04:00
40 changed files with 801 additions and 769 deletions

View File

@@ -4,7 +4,7 @@ import pytz
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist
from django.db.models import ManyToManyField
from django.db.models import ManyToManyField, ProtectedError
from django.http import Http404
from rest_framework.exceptions import APIException
from rest_framework.permissions import BasePermission
@@ -274,6 +274,19 @@ class ModelViewSet(_ModelViewSet):
# Fall back to the hard-coded serializer class
return self.serializer_class
def dispatch(self, request, *args, **kwargs):
try:
return super().dispatch(request, *args, **kwargs)
except ProtectedError as e:
models = ['{} ({})'.format(o, o._meta) for o in e.protected_objects.all()]
msg = 'Unable to delete object. The following dependent objects were found: {}'.format(', '.join(models))
return self.finalize_response(
request,
Response({'detail': msg}, status=409),
*args,
**kwargs
)
def list(self, *args, **kwargs):
"""
Call to super to allow for caching

View File

@@ -2,6 +2,7 @@ from django.conf import settings
from django.db import ProgrammingError
from django.http import Http404, HttpResponseRedirect
from django.urls import reverse
import urllib
from .views import server_error
@@ -22,7 +23,12 @@ class LoginRequiredMiddleware(object):
# performs its own authentication. Also metrics can be read without login.
api_path = reverse('api-root')
if not request.path_info.startswith((api_path, '/metrics')) and request.path_info != settings.LOGIN_URL:
return HttpResponseRedirect('{}?next={}'.format(settings.LOGIN_URL, request.path_info))
return HttpResponseRedirect(
'{}?next={}'.format(
settings.LOGIN_URL,
urllib.parse.quote(request.get_full_path_info())
)
)
return self.get_response(request)

View File

@@ -1,5 +1,4 @@
import sys
from collections import OrderedDict
from copy import deepcopy
from django.conf import settings
@@ -12,7 +11,7 @@ from django.forms import CharField, Form, ModelMultipleChoiceField, MultipleHidd
from django.http import HttpResponse, HttpResponseServerError
from django.shortcuts import get_object_or_404, redirect, render
from django.template import loader
from django.template.exceptions import TemplateDoesNotExist, TemplateSyntaxError
from django.template.exceptions import TemplateDoesNotExist
from django.urls import reverse
from django.utils.html import escape
from django.utils.http import is_safe_url
@@ -23,6 +22,7 @@ from django.views.generic import View
from django_tables2 import RequestConfig
from extras.models import CustomField, CustomFieldValue, ExportTemplate
from extras.querysets import CustomFieldQueryset
from utilities.forms import BootstrapMixin, CSVDataField
from utilities.utils import csv_format
from .error_handlers import handle_protectederror
@@ -30,23 +30,6 @@ from .forms import ConfirmationForm
from .paginator import EnhancedPaginator
class CustomFieldQueryset:
"""
Annotate custom fields on objects within a QuerySet.
"""
def __init__(self, queryset, custom_fields):
self.queryset = queryset
self.model = queryset.model
self.custom_fields = custom_fields
def __iter__(self):
for obj in self.queryset:
values_dict = {cfv.field_id: cfv.value for cfv in obj.custom_field_values.all()}
obj.custom_fields = OrderedDict([(field, values_dict.get(field.pk)) for field in self.custom_fields])
yield obj
class GetReturnURLMixin(object):
"""
Provides logic for determining where a user should be redirected after processing a form.
@@ -115,8 +98,9 @@ class ObjectListView(View):
self.queryset = self.filter(request.GET, self.queryset).qs
# If this type of object has one or more custom fields, prefetch any relevant custom field values
custom_fields = CustomField.objects.filter(obj_type=ContentType.objects.get_for_model(model))\
.prefetch_related('choices')
custom_fields = CustomField.objects.filter(
obj_type=ContentType.objects.get_for_model(model)
).prefetch_related('choices')
if custom_fields:
self.queryset = self.queryset.prefetch_related('custom_field_values')
@@ -126,10 +110,12 @@ class ObjectListView(View):
queryset = CustomFieldQueryset(self.queryset, custom_fields) if custom_fields else self.queryset
try:
return et.render_to_response(queryset)
except TemplateSyntaxError:
except Exception as e:
messages.error(
request,
"There was an error rendering the selected export template ({}).".format(et.name)
"There was an error rendering the selected export template ({}): {}".format(
et.name, e
)
)
# Fall back to built-in CSV formatting if export requested but no template specified