Update dependencies

Update DRF to resolve https://github.com/advisories/GHSA-fx83-3ph3-9j2q

-> https://www.django-rest-framework.org/community/3.9-announcement/
replaces `list_route` and `detail_route`

Update autoupdate submodule
This commit is contained in:
Finn Stutzenstein 2021-04-12 08:20:06 +02:00
parent 963986b91d
commit e3c627b504
No known key found for this signature in database
GPG Key ID: 9042F605C6324654
11 changed files with 66 additions and 82 deletions

@ -1 +1 @@
Subproject commit c1211219d81965b10780ecfa5a1de31f9e30d31e Subproject commit 3ede3a13cb1b236b3c0d50b9641c318f2eb2727f

View File

@ -12,8 +12,7 @@ from openslides.utils.rest_api import (
Response, Response,
UpdateModelMixin, UpdateModelMixin,
ValidationError, ValidationError,
detail_route, action,
list_route,
status, status,
) )
from openslides.utils.views import TreeSortMixin from openslides.utils.views import TreeSortMixin
@ -143,7 +142,7 @@ class ItemViewSet(ModelViewSet, TreeSortMixin):
return response return response
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def numbering(self, request): def numbering(self, request):
""" """
Auto numbering of the agenda according to the config. Manually added Auto numbering of the agenda according to the config. Manually added
@ -157,7 +156,7 @@ class ItemViewSet(ModelViewSet, TreeSortMixin):
Item.objects.number_all(numeral_system=config["agenda_numeral_system"]) Item.objects.number_all(numeral_system=config["agenda_numeral_system"])
return Response({"detail": "The agenda has been numbered."}) return Response({"detail": "The agenda has been numbered."})
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def sort(self, request): def sort(self, request):
""" """
Sorts the whole agenda represented in a tree of ids. The request data should be a list (the root) Sorts the whole agenda represented in a tree of ids. The request data should be a list (the root)
@ -172,7 +171,7 @@ class ItemViewSet(ModelViewSet, TreeSortMixin):
""" """
return self.sort_tree(request, Item, "weight", "parent_id") return self.sort_tree(request, Item, "weight", "parent_id")
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def assign(self, request): def assign(self, request):
""" """
@ -294,7 +293,7 @@ class ListOfSpeakersViewSet(UpdateModelMixin, TreeSortMixin, GenericViewSet):
result = False result = False
return result return result
@detail_route(methods=["POST", "PATCH", "DELETE"]) @action(detail=True, methods=["POST", "PATCH", "DELETE"])
@transaction.atomic @transaction.atomic
def manage_speaker(self, request, pk=None): def manage_speaker(self, request, pk=None):
""" """
@ -443,7 +442,7 @@ class ListOfSpeakersViewSet(UpdateModelMixin, TreeSortMixin, GenericViewSet):
return Response() return Response()
@detail_route(methods=["PUT", "DELETE"]) @action(detail=True, methods=["PUT", "DELETE"])
def speak(self, request, pk=None): def speak(self, request, pk=None):
""" """
Special view endpoint to begin and end speech of speakers. Send PUT Special view endpoint to begin and end speech of speakers. Send PUT
@ -494,7 +493,7 @@ class ListOfSpeakersViewSet(UpdateModelMixin, TreeSortMixin, GenericViewSet):
# Initiate response. # Initiate response.
return Response({"detail": message}) return Response({"detail": message})
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
def sort_speakers(self, request, pk=None): def sort_speakers(self, request, pk=None):
""" """
Special view endpoint to sort the list of speakers. Special view endpoint to sort the list of speakers.
@ -533,7 +532,7 @@ class ListOfSpeakersViewSet(UpdateModelMixin, TreeSortMixin, GenericViewSet):
# Initiate response. # Initiate response.
return Response({"detail": "List of speakers successfully sorted."}) return Response({"detail": "List of speakers successfully sorted."})
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
def readd_last_speaker(self, request, pk=None): def readd_last_speaker(self, request, pk=None):
""" """
Special view endpoint to re-add the last finished speaker to the list of speakers. Special view endpoint to re-add the last finished speaker to the list of speakers.
@ -572,7 +571,7 @@ class ListOfSpeakersViewSet(UpdateModelMixin, TreeSortMixin, GenericViewSet):
return Response() return Response()
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def delete_all_speakers(self, request): def delete_all_speakers(self, request):
Speaker.objects.all().delete() Speaker.objects.all().delete()
inform_changed_data(ListOfSpeakers.objects.all()) inform_changed_data(ListOfSpeakers.objects.all())

View File

@ -7,12 +7,7 @@ from openslides.core.config import config
from openslides.poll.views import BaseOptionViewSet, BasePollViewSet, BaseVoteViewSet from openslides.poll.views import BaseOptionViewSet, BasePollViewSet, BaseVoteViewSet
from openslides.utils.auth import has_perm from openslides.utils.auth import has_perm
from openslides.utils.autoupdate import inform_changed_data from openslides.utils.autoupdate import inform_changed_data
from openslides.utils.rest_api import ( from openslides.utils.rest_api import ModelViewSet, Response, ValidationError, action
ModelViewSet,
Response,
ValidationError,
detail_route,
)
from openslides.utils.utils import is_int from openslides.utils.utils import is_int
from .models import ( from .models import (
@ -63,7 +58,7 @@ class AssignmentViewSet(ModelViewSet):
def perform_create(self, serializer): def perform_create(self, serializer):
serializer.save(request_user=self.request.user) serializer.save(request_user=self.request.user)
@detail_route(methods=["post", "delete"]) @action(detail=True, methods=["post", "delete"])
def candidature_self(self, request, pk=None): def candidature_self(self, request, pk=None):
""" """
View to nominate self as candidate (POST) or withdraw own View to nominate self as candidate (POST) or withdraw own
@ -116,7 +111,7 @@ class AssignmentViewSet(ModelViewSet):
assignment.remove_candidate(request.user) assignment.remove_candidate(request.user)
return "You have withdrawn your candidature successfully." return "You have withdrawn your candidature successfully."
@detail_route(methods=["post", "delete"]) @action(detail=True, methods=["post", "delete"])
def candidature_other(self, request, pk=None): def candidature_other(self, request, pk=None):
""" """
View to nominate other users (POST) or delete their candidature View to nominate other users (POST) or delete their candidature
@ -186,7 +181,7 @@ class AssignmentViewSet(ModelViewSet):
{"detail": "Candidate {0} was withdrawn successfully.", "args": [str(user)]} {"detail": "Candidate {0} was withdrawn successfully.", "args": [str(user)]}
) )
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def sort_related_users(self, request, pk=None): def sort_related_users(self, request, pk=None):
""" """
Special view endpoint to sort the assignment related users. Special view endpoint to sort the assignment related users.

View File

@ -14,7 +14,7 @@ from openslides.utils.rest_api import (
GenericViewSet, GenericViewSet,
ModelViewSet, ModelViewSet,
Response, Response,
detail_route, action,
status, status,
) )
@ -52,7 +52,7 @@ class ChatGroupViewSet(ModelViewSet):
inform_changed_data(ChatMessage.objects.filter(chatgroup=self.get_object())) inform_changed_data(ChatMessage.objects.filter(chatgroup=self.get_object()))
return response return response
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
def clear(self, request, *args, **kwargs): def clear(self, request, *args, **kwargs):
""" """
Deletes all chat messages of the group. Deletes all chat messages of the group.

View File

@ -35,8 +35,7 @@ from ..utils.rest_api import (
ModelViewSet, ModelViewSet,
Response, Response,
ValidationError, ValidationError,
detail_route, action,
list_route,
) )
from .config import config from .config import config
from .exceptions import ConfigError, ConfigNotFound from .exceptions import ConfigError, ConfigNotFound
@ -186,7 +185,7 @@ class ProjectorViewSet(ModelViewSet):
projection_default.save() projection_default.save()
return super(ProjectorViewSet, self).destroy(*args, **kwargs) return super(ProjectorViewSet, self).destroy(*args, **kwargs)
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def project(self, request, pk): def project(self, request, pk):
""" """
Sets the `elements` and `elements_preview` and adds one item to the Sets the `elements` and `elements_preview` and adds one item to the
@ -237,7 +236,7 @@ class ProjectorViewSet(ModelViewSet):
projector.save() projector.save()
return Response() return Response()
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def control_view(self, request, pk): def control_view(self, request, pk):
""" """
REST API operation to control the projector view, i. e. scale and REST API operation to control the projector view, i. e. scale and
@ -296,7 +295,7 @@ class ProjectorViewSet(ModelViewSet):
inform_changed_data(projector_instance) inform_changed_data(projector_instance)
return Response() return Response()
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def set_scroll(self, request, pk): def set_scroll(self, request, pk):
""" """
REST API operation to scroll the projector. REST API operation to scroll the projector.
@ -315,7 +314,7 @@ class ProjectorViewSet(ModelViewSet):
{"detail": "Setting scroll to {0} was successful.", "args": [request.data]} {"detail": "Setting scroll to {0} was successful.", "args": [request.data]}
) )
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def set_reference_projector(self, request, pk): def set_reference_projector(self, request, pk):
""" """
REST API operation to set the projector with the given pk as the new reference projector for all projectors. REST API operation to set the projector with the given pk as the new reference projector for all projectors.
@ -434,7 +433,7 @@ class ConfigViewSet(ModelViewSet):
# Return response. # Return response.
return Response({"key": key, "value": value}) return Response({"key": key, "value": value})
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def bulk_update(self, request): def bulk_update(self, request):
""" """
Updates many config variables: Updates many config variables:
@ -467,7 +466,7 @@ class ConfigViewSet(ModelViewSet):
return Response({"errors": errors}) return Response({"errors": errors})
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def reset_groups(self, request): def reset_groups(self, request):
""" """
Resets multiple groups. The request data contains all Resets multiple groups. The request data contains all

View File

@ -15,7 +15,7 @@ from openslides.utils.rest_api import (
ModelViewSet, ModelViewSet,
Response, Response,
ValidationError, ValidationError,
list_route, action,
status, status,
) )
@ -178,7 +178,7 @@ class MediafileViewSet(ModelViewSet):
inform_changed_data(self.get_object().get_children_deep()) inform_changed_data(self.get_object().get_children_deep())
return response return response
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def move(self, request): def move(self, request):
""" """
{ {
@ -249,7 +249,7 @@ class MediafileViewSet(ModelViewSet):
return Response() return Response()
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def bulk_delete(self, request): def bulk_delete(self, request):
""" """
Deletes mediafiles *from one directory*. Expected data: Deletes mediafiles *from one directory*. Expected data:

View File

@ -16,14 +16,7 @@ from ..core.config import config
from ..core.models import Tag from ..core.models import Tag
from ..utils.auth import has_perm, in_some_groups from ..utils.auth import has_perm, in_some_groups
from ..utils.autoupdate import inform_changed_data, inform_deleted_data from ..utils.autoupdate import inform_changed_data, inform_deleted_data
from ..utils.rest_api import ( from ..utils.rest_api import ModelViewSet, Response, ReturnDict, ValidationError, action
ModelViewSet,
Response,
ReturnDict,
ValidationError,
detail_route,
list_route,
)
from ..utils.views import TreeSortMixin from ..utils.views import TreeSortMixin
from .models import ( from .models import (
Category, Category,
@ -316,7 +309,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
# We do not add serializer.data to response so nobody gets unrestricted data here. # We do not add serializer.data to response so nobody gets unrestricted data here.
return Response() return Response()
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def sort(self, request): def sort(self, request):
""" """
Sorts all motions represented in a tree of ids. The request data should be a list (the root) Sorts all motions represented in a tree of ids. The request data should be a list (the root)
@ -331,7 +324,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
""" """
return self.sort_tree(request, Motion, "weight", "sort_parent_id") return self.sort_tree(request, Motion, "weight", "sort_parent_id")
@detail_route(methods=["POST", "DELETE"]) @action(detail=True, methods=["POST", "DELETE"])
def manage_comments(self, request, pk=None): def manage_comments(self, request, pk=None):
""" """
Create, update and delete motion comments. Create, update and delete motion comments.
@ -402,7 +395,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
return Response({"detail": message}) return Response({"detail": message})
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def manage_multiple_submitters(self, request): def manage_multiple_submitters(self, request):
""" """
@ -488,7 +481,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
} }
) )
@detail_route(methods=["post", "delete"]) @action(detail=True, methods=["post", "delete"])
def support(self, request, pk=None): def support(self, request, pk=None):
""" """
Special view endpoint to support a motion or withdraw support Special view endpoint to support a motion or withdraw support
@ -534,7 +527,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
# Initiate response. # Initiate response.
return Response({"detail": message}) return Response({"detail": message})
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def manage_multiple_category(self, request): def manage_multiple_category(self, request):
""" """
@ -621,7 +614,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
} }
) )
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def manage_multiple_motion_block(self, request): def manage_multiple_motion_block(self, request):
""" """
@ -715,7 +708,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
} }
) )
@detail_route(methods=["put"]) @action(detail=True, methods=["put"])
def set_state(self, request, pk=None): def set_state(self, request, pk=None):
""" """
Special view endpoint to set and reset a state of a motion. Special view endpoint to set and reset a state of a motion.
@ -774,7 +767,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
return Response({"detail": message}) return Response({"detail": message})
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def manage_multiple_state(self, request): def manage_multiple_state(self, request):
""" """
@ -864,7 +857,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
} }
) )
@detail_route(methods=["put"]) @action(detail=True, methods=["put"])
def set_recommendation(self, request, pk=None): def set_recommendation(self, request, pk=None):
""" """
Special view endpoint to set a recommendation of a motion. Special view endpoint to set a recommendation of a motion.
@ -926,7 +919,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
} }
) )
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def manage_multiple_recommendation(self, request): def manage_multiple_recommendation(self, request):
""" """
@ -1020,7 +1013,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
} }
) )
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def follow_recommendation(self, request, pk=None): def follow_recommendation(self, request, pk=None):
motion = self.get_object() motion = self.get_object()
if motion.recommendation is None: if motion.recommendation is None:
@ -1048,7 +1041,7 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
return Response({"detail": "Recommendation followed successfully."}) return Response({"detail": "Recommendation followed successfully."})
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def manage_multiple_tags(self, request): def manage_multiple_tags(self, request):
""" """
@ -1407,7 +1400,7 @@ class MotionCommentSectionViewSet(ModelViewSet):
inform_changed_data(MotionComment.objects.filter(section=section)) inform_changed_data(MotionComment.objects.filter(section=section))
return response return response
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def sort(self, request, *args, **kwargs): def sort(self, request, *args, **kwargs):
""" """
Changes the sorting of comment sections. Every id must be given exactly once. Changes the sorting of comment sections. Every id must be given exactly once.
@ -1494,7 +1487,7 @@ class CategoryViewSet(TreeSortMixin, ModelViewSet):
result = False result = False
return result return result
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def sort_categories(self, request): def sort_categories(self, request):
""" """
Sorts all categoreis represented in a tree of ids. The request data should be Sorts all categoreis represented in a tree of ids. The request data should be
@ -1510,7 +1503,7 @@ class CategoryViewSet(TreeSortMixin, ModelViewSet):
""" """
return self.sort_tree(request, Category, "weight", "parent_id") return self.sort_tree(request, Category, "weight", "parent_id")
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
@transaction.atomic @transaction.atomic
def sort_motions(self, request, pk=None): def sort_motions(self, request, pk=None):
""" """
@ -1556,7 +1549,7 @@ class CategoryViewSet(TreeSortMixin, ModelViewSet):
inform_changed_data(motions) inform_changed_data(motions)
return Response() return Response()
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def numbering(self, request, pk=None): def numbering(self, request, pk=None):
""" """
Special view endpoint to number all motions in this category and all Special view endpoint to number all motions in this category and all
@ -1609,7 +1602,7 @@ class MotionBlockViewSet(ModelViewSet):
def perform_create(self, serializer): def perform_create(self, serializer):
serializer.save(request_user=self.request.user) serializer.save(request_user=self.request.user)
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def follow_recommendations(self, request, pk=None): def follow_recommendations(self, request, pk=None):
""" """
View to set the states of all motions of this motion block each to View to set the states of all motions of this motion block each to

View File

@ -14,7 +14,7 @@ from openslides.utils.rest_api import (
ModelViewSet, ModelViewSet,
Response, Response,
ValidationError, ValidationError,
detail_route, action,
) )
from .models import BasePoll from .models import BasePoll
@ -119,7 +119,7 @@ class BasePollViewSet(ModelViewSet):
# The implementation can be found in concret view set e. g. MotionPollViewSet. # The implementation can be found in concret view set e. g. MotionPollViewSet.
pass pass
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
@transaction.atomic @transaction.atomic
def start(self, request, pk): def start(self, request, pk):
poll = self.get_object() poll = self.get_object()
@ -132,7 +132,7 @@ class BasePollViewSet(ModelViewSet):
self.extend_history_information(["Voting started"]) self.extend_history_information(["Voting started"])
return Response() return Response()
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
@transaction.atomic @transaction.atomic
def stop(self, request, pk): def stop(self, request, pk):
poll = self.get_object() poll = self.get_object()
@ -152,7 +152,7 @@ class BasePollViewSet(ModelViewSet):
self.extend_history_information(["Voting stopped"]) self.extend_history_information(["Voting stopped"])
return Response() return Response()
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
@transaction.atomic @transaction.atomic
def publish(self, request, pk): def publish(self, request, pk):
poll = self.get_object() poll = self.get_object()
@ -172,7 +172,7 @@ class BasePollViewSet(ModelViewSet):
inform_changed_data(poll.get_options()) inform_changed_data(poll.get_options())
return Response() return Response()
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
@transaction.atomic @transaction.atomic
def pseudoanonymize(self, request, pk): def pseudoanonymize(self, request, pk):
poll = self.get_object() poll = self.get_object()
@ -188,7 +188,7 @@ class BasePollViewSet(ModelViewSet):
self.extend_history_information(["Voting anonymized"]) self.extend_history_information(["Voting anonymized"])
return Response() return Response()
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
@transaction.atomic @transaction.atomic
def reset(self, request, pk): def reset(self, request, pk):
poll = self.get_object() poll = self.get_object()
@ -196,7 +196,7 @@ class BasePollViewSet(ModelViewSet):
self.extend_history_information(["Voting reset"]) self.extend_history_information(["Voting reset"])
return Response() return Response()
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
@transaction.atomic @transaction.atomic
def vote(self, request, pk): def vote(self, request, pk):
""" """
@ -254,7 +254,7 @@ class BasePollViewSet(ModelViewSet):
return Response() return Response()
@detail_route(methods=["POST"]) @action(detail=True, methods=["POST"])
@transaction.atomic @transaction.atomic
def refresh(self, request, pk): def refresh(self, request, pk):
poll = self.get_object() poll = self.get_object()

View File

@ -42,8 +42,7 @@ from ..utils.rest_api import (
Response, Response,
SimpleMetadata, SimpleMetadata,
ValidationError, ValidationError,
detail_route, action,
list_route,
status, status,
) )
from ..utils.validate import validate_json from ..utils.validate import validate_json
@ -260,7 +259,7 @@ class UserViewSet(ModelViewSet):
self.perform_destroy(instance) self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
def reset_password(self, request, pk=None): def reset_password(self, request, pk=None):
""" """
View to reset the password of the given user (by url) using a provided password. View to reset the password of the given user (by url) using a provided password.
@ -287,7 +286,7 @@ class UserViewSet(ModelViewSet):
user.save() user.save()
return Response({"detail": "Password successfully reset."}) return Response({"detail": "Password successfully reset."})
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def bulk_generate_passwords(self, request): def bulk_generate_passwords(self, request):
""" """
Generates new random passwords for many users. The request user is excluded Generates new random passwords for many users. The request user is excluded
@ -307,7 +306,7 @@ class UserViewSet(ModelViewSet):
user.save() user.save()
return Response() return Response()
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def bulk_reset_passwords_to_default(self, request): def bulk_reset_passwords_to_default(self, request):
""" """
resets the password of all given users to their default ones. The resets the password of all given users to their default ones. The
@ -339,7 +338,7 @@ class UserViewSet(ModelViewSet):
user.save() user.save()
return Response() return Response()
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def bulk_set_state(self, request): def bulk_set_state(self, request):
""" """
Sets the "state" of may users. The "state" means boolean attributes like active Sets the "state" of may users. The "state" means boolean attributes like active
@ -377,7 +376,7 @@ class UserViewSet(ModelViewSet):
return Response() return Response()
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def bulk_alter_groups(self, request): def bulk_alter_groups(self, request):
""" """
Adds or removes groups from given users. The request user is excluded. Adds or removes groups from given users. The request user is excluded.
@ -410,7 +409,7 @@ class UserViewSet(ModelViewSet):
inform_changed_data(users) inform_changed_data(users)
return Response() return Response()
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def bulk_delete(self, request): def bulk_delete(self, request):
""" """
Deletes many users. The request user will be excluded. Expected data: Deletes many users. The request user will be excluded. Expected data:
@ -437,7 +436,7 @@ class UserViewSet(ModelViewSet):
queryset = queryset.filter(auth_type=auth_type) queryset = queryset.filter(auth_type=auth_type)
return queryset.exclude(pk=request.user.id).filter(pk__in=ids) return queryset.exclude(pk=request.user.id).filter(pk__in=ids)
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def mass_import(self, request): def mass_import(self, request):
""" """
@ -490,7 +489,7 @@ class UserViewSet(ModelViewSet):
} }
) )
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
def mass_invite_email(self, request): def mass_invite_email(self, request):
""" """
Endpoint to send invitation emails to all given users (by id). Returns the Endpoint to send invitation emails to all given users (by id). Returns the
@ -664,7 +663,7 @@ class GroupViewSet(ModelViewSet):
inform_changed_data(affected_users) inform_changed_data(affected_users)
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
@detail_route(methods=["post"]) @action(detail=True, methods=["post"])
@transaction.atomic @transaction.atomic
def set_permission(self, request, *args, **kwargs): def set_permission(self, request, *args, **kwargs):
""" """
@ -764,7 +763,7 @@ class PersonalNoteViewSet(ModelViewSet):
result = False result = False
return result return result
@list_route(methods=["post"]) @action(detail=False, methods=["post"])
@transaction.atomic @transaction.atomic
def create_or_update(self, request, *args, **kwargs): def create_or_update(self, request, *args, **kwargs):
""" """

View File

@ -3,7 +3,7 @@ from typing import Any, Dict, Iterable, Type
from django.db.models import Model from django.db.models import Model
from rest_framework import status from rest_framework import status
from rest_framework.decorators import detail_route, list_route from rest_framework.decorators import action
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.metadata import SimpleMetadata from rest_framework.metadata import SimpleMetadata
from rest_framework.mixins import ( from rest_framework.mixins import (
@ -43,9 +43,8 @@ from . import logging
__all__ = [ __all__ = [
"APIException", "APIException",
"detail_route", "action",
"DecimalField", "DecimalField",
"list_route",
"SimpleMetadata", "SimpleMetadata",
"DestroyModelMixin", "DestroyModelMixin",
"CharField", "CharField",

View File

@ -12,7 +12,7 @@ aioredis>=1.1.0,<1.3
bleach>=3.3.0 bleach>=3.3.0
daphne>=2.2,<2.5 daphne>=2.2,<2.5
Django>=2.1,<2.3 Django>=2.1,<2.3
djangorestframework>=3.9.4,<3.10 djangorestframework>=3.11.2
jsonfield2>=3.0,<3.1 jsonfield2>=3.0,<3.1
attrs>=19.2.0 attrs>=19.2.0
jsonschema>=3.0,<3.1 jsonschema>=3.0,<3.1