You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.6 KiB
179 lines
5.6 KiB
from django.db import transaction
|
|
from django.db.models import Q
|
|
|
|
from rest_framework import mixins
|
|
from rest_framework import viewsets
|
|
from rest_framework import permissions
|
|
from rest_framework.response import Response
|
|
from rest_framework.decorators import action
|
|
from rest_framework.generics import get_object_or_404
|
|
|
|
from dynamic_preferences import models
|
|
from dynamic_preferences import exceptions
|
|
from dynamic_preferences.settings import preferences_settings
|
|
|
|
from . import serializers
|
|
|
|
|
|
class PreferenceViewSet(
|
|
mixins.UpdateModelMixin,
|
|
mixins.ListModelMixin,
|
|
mixins.RetrieveModelMixin,
|
|
viewsets.GenericViewSet,
|
|
):
|
|
"""
|
|
- list preferences
|
|
- detail given preference
|
|
- batch update preferences
|
|
- update a single preference
|
|
"""
|
|
|
|
def get_queryset(self):
|
|
"""
|
|
We just ensure preferences are actually populated before fetching
|
|
from db
|
|
"""
|
|
self.init_preferences()
|
|
queryset = super(PreferenceViewSet, self).get_queryset()
|
|
|
|
section = self.request.query_params.get("section")
|
|
if section:
|
|
queryset = queryset.filter(section=section)
|
|
|
|
return queryset
|
|
|
|
def get_manager(self):
|
|
return self.queryset.model.registry.manager()
|
|
|
|
def init_preferences(self):
|
|
manager = self.get_manager()
|
|
manager.all()
|
|
|
|
def get_object(self):
|
|
"""
|
|
Returns the object the view is displaying.
|
|
You may want to override this if you need to provide non-standard
|
|
queryset lookups. Eg if objects are referenced using multiple
|
|
keyword arguments in the url conf.
|
|
"""
|
|
queryset = self.filter_queryset(self.get_queryset())
|
|
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
|
identifier = self.kwargs[lookup_url_kwarg]
|
|
section, name = self.get_section_and_name(identifier)
|
|
filter_kwargs = {"section": section, "name": name}
|
|
obj = get_object_or_404(queryset, **filter_kwargs)
|
|
|
|
# May raise a permission denied
|
|
self.check_object_permissions(self.request, obj)
|
|
|
|
return obj
|
|
|
|
def get_section_and_name(self, identifier):
|
|
try:
|
|
section, name = identifier.split(preferences_settings.SECTION_KEY_SEPARATOR)
|
|
except ValueError:
|
|
# no section given
|
|
section, name = None, identifier
|
|
|
|
return section, name
|
|
|
|
@action(detail=False, methods=["post"])
|
|
@transaction.atomic
|
|
def bulk(self, request, *args, **kwargs):
|
|
"""
|
|
Update multiple preferences at once
|
|
|
|
this is a long method because we ensure everything is valid
|
|
before actually persisting the changes
|
|
"""
|
|
manager = self.get_manager()
|
|
errors = {}
|
|
preferences = []
|
|
payload = request.data
|
|
|
|
# first, we check updated preferences actually exists in the registry
|
|
try:
|
|
for identifier, value in payload.items():
|
|
try:
|
|
preferences.append(self.queryset.model.registry.get(identifier))
|
|
except exceptions.NotFoundInRegistry:
|
|
errors[identifier] = "invalid preference"
|
|
except (TypeError, AttributeError):
|
|
return Response("invalid payload", status=400)
|
|
|
|
if errors:
|
|
return Response(errors, status=400)
|
|
|
|
# now, we generate an optimized Q objects to retrieve all matching
|
|
# preferences at once from database
|
|
queries = [Q(section=p.section.name, name=p.name) for p in preferences]
|
|
|
|
query = queries[0]
|
|
for q in queries[1:]:
|
|
query |= q
|
|
preferences_qs = self.get_queryset().filter(query)
|
|
|
|
# next, we generate a serializer for each database preference
|
|
serializer_objects = []
|
|
for p in preferences_qs:
|
|
s = self.get_serializer_class()(
|
|
p, data={"value": payload[p.preference.identifier()]}
|
|
)
|
|
serializer_objects.append(s)
|
|
|
|
validation_errors = {}
|
|
|
|
# we check if any serializer is invalid
|
|
for s in serializer_objects:
|
|
if s.is_valid():
|
|
continue
|
|
validation_errors[s.instance.preference.identifier()] = s.errors
|
|
|
|
if validation_errors:
|
|
return Response(validation_errors, status=400)
|
|
|
|
for s in serializer_objects:
|
|
s.save()
|
|
|
|
return Response(
|
|
[s.data for s in serializer_objects],
|
|
status=200,
|
|
)
|
|
|
|
|
|
class GlobalPreferencePermission(permissions.DjangoModelPermissions):
|
|
perms_map = {
|
|
"GET": ["%(app_label)s.change_%(model_name)s"],
|
|
"OPTIONS": ["%(app_label)s.change_%(model_name)s"],
|
|
"HEAD": ["%(app_label)s.change_%(model_name)s"],
|
|
"POST": ["%(app_label)s.change_%(model_name)s"],
|
|
"PUT": ["%(app_label)s.change_%(model_name)s"],
|
|
"PATCH": ["%(app_label)s.change_%(model_name)s"],
|
|
"DELETE": ["%(app_label)s.change_%(model_name)s"],
|
|
}
|
|
|
|
|
|
class GlobalPreferencesViewSet(PreferenceViewSet):
|
|
queryset = models.GlobalPreferenceModel.objects.all()
|
|
serializer_class = serializers.GlobalPreferenceSerializer
|
|
permission_classes = [GlobalPreferencePermission]
|
|
|
|
|
|
class PerInstancePreferenceViewSet(PreferenceViewSet):
|
|
def get_manager(self):
|
|
return self.queryset.model.registry.manager(
|
|
instance=self.get_related_instance()
|
|
)
|
|
|
|
def get_queryset(self):
|
|
return (
|
|
super(PerInstancePreferenceViewSet, self)
|
|
.get_queryset()
|
|
.filter(instance=self.get_related_instance())
|
|
)
|
|
|
|
def get_related_instance(self):
|
|
"""
|
|
Override this to the instance bound to the preferences
|
|
"""
|
|
raise NotImplementedError
|