Skip to content
Snippets Groups Projects

Add API documentation

Merged Konrad Mohrfeldt requested to merge feature/api-docs into master
1 file
+ 4
0
Compare changes
  • Side-by-side
  • Inline
+ 305
299
@@ -21,7 +21,9 @@
import json
import logging
from datetime import date, datetime, time
from textwrap import dedent
from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_view
from rest_framework import mixins, permissions, status, viewsets
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.response import Response
@@ -47,11 +49,15 @@ from program.models import (
)
from program.serializers import (
CategorySerializer,
ErrorSerializer,
FundingCategorySerializer,
HostSerializer,
LanguageSerializer,
MusicFocusSerializer,
NoteSerializer,
ScheduleConflictResponseSerializer,
ScheduleCreateUpdateRequestSerializer,
ScheduleDryRunResponseSerializer,
ScheduleSerializer,
ShowSerializer,
TimeSlotSerializer,
@@ -59,7 +65,12 @@ from program.serializers import (
TypeSerializer,
UserSerializer,
)
from program.utils import get_pk_and_slug, get_values, parse_date
from program.utils import (
DisabledObjectPermissionCheckMixin,
NestedObjectFinderMixin,
get_values,
parse_date,
)
logger = logging.getLogger(__name__)
@@ -187,19 +198,36 @@ def json_playout(request):
)
@extend_schema_view(
create=extend_schema(summary="Create a new user."),
retrieve=extend_schema(
summary="Retrieve a single user.",
description="Non-admin users may only retrieve their own user record.",
),
update=extend_schema(
summary="Update an existing user.",
description="Non-admin users may only update their own user record.",
),
partial_update=extend_schema(
summary="Partially update an existing user.",
description="Non-admin users may only update their own user record.",
),
list=extend_schema(
summary="List all users.",
description=(
"The returned list of records will only contain a single record "
"for non-admin users which is their own user account."
),
),
)
class APIUserViewSet(
DisabledObjectPermissionCheckMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
"""
Returns a list of users.
Only returns the user that is currently authenticated unless the user is a superuser.
"""
permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
serializer_class = UserSerializer
queryset = User.objects.all()
@@ -213,23 +241,9 @@ class APIUserViewSet(
return queryset
def retrieve(self, request, *args, **kwargs):
"""Returns a single user."""
pk = get_values(self.kwargs, "pk")
# Common users only see themselves
if not request.user.is_superuser and pk != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
def create(self, request, *args, **kwargs):
"""
Create a User.
Only superusers may create users.
Only admins may create users.
"""
if not request.user.is_superuser:
@@ -243,51 +257,38 @@ class APIUserViewSet(
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def update(self, request, *args, **kwargs):
"""
Updates the user’s data.
Non-superusers may not be able to edit all of the available data.
"""
pk = get_values(self.kwargs, "pk")
serializer = UserSerializer(data=request.data)
# Common users may only edit themselves
if not request.user.is_superuser and pk != request.user.id:
return Response(
serializer.initial_data, status=status.HTTP_401_UNAUTHORIZED
)
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(
user, data=request.data, context={"user": request.user}
)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class APIShowViewSet(viewsets.ModelViewSet):
"""
Returns a list of available shows.
Only superusers may add and delete shows.
"""
@extend_schema_view(
create=extend_schema(summary="Create a new show."),
retrieve=extend_schema(summary="Retrieve a single show."),
update=extend_schema(summary="Update an existing show."),
partial_update=extend_schema(summary="Partially update an existing show."),
destroy=extend_schema(summary="Delete an existing show."),
list=extend_schema(summary="List all shows."),
)
class APIShowViewSet(DisabledObjectPermissionCheckMixin, viewsets.ModelViewSet):
queryset = Show.objects.all()
serializer_class = ShowSerializer
permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
pagination_class = LimitOffsetPagination
filterset_class = filters.ShowFilterSet
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
lookup_arg = self.kwargs[lookup_url_kwarg]
# allow object retrieval through id or slug
try:
filter_kwargs = {self.lookup_field: int(lookup_arg)}
except ValueError:
filter_kwargs = {"slug": lookup_arg}
obj = get_object_or_404(queryset, **filter_kwargs)
self.check_object_permissions(self.request, obj)
return obj
def create(self, request, *args, **kwargs):
"""
Create a show.
Only superusers may create a show.
Only admins may create a show.
"""
if not request.user.is_superuser:
@@ -301,28 +302,9 @@ class APIShowViewSet(viewsets.ModelViewSet):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def retrieve(self, request, *args, **kwargs):
"""Returns a single show"""
pk, slug = get_pk_and_slug(self.kwargs)
show = (
get_object_or_404(Show, pk=pk)
if pk
else get_object_or_404(Show, slug=slug)
if slug
else None
)
serializer = ShowSerializer(show)
return Response(serializer.data)
def update(self, request, *args, **kwargs):
"""
Update a show.
Common users may only update shows they own.
Non-admin users may only update shows they own.
"""
pk = get_values(self.kwargs, "pk")
@@ -332,7 +314,7 @@ class APIShowViewSet(viewsets.ModelViewSet):
):
return Response(status=status.HTTP_401_UNAUTHORIZED)
show = get_object_or_404(Show, pk=pk)
show = self.get_object()
serializer = ShowSerializer(
show, data=request.data, context={"user": request.user}
)
@@ -348,60 +330,139 @@ class APIShowViewSet(viewsets.ModelViewSet):
def destroy(self, request, *args, **kwargs):
"""
Delete a show.
Only superusers may delete shows.
Only admins may delete shows.
"""
if not request.user.is_superuser:
return Response(status=status.HTTP_401_UNAUTHORIZED)
pk = get_values(self.kwargs, "pk")
Show.objects.get(pk=pk).delete()
self.get_object().delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class APIScheduleViewSet(viewsets.ModelViewSet):
"""
Returns a list of schedules.
Only superusers may create and update schedules.
"""
@extend_schema_view(
create=extend_schema(
summary="Create a new schedule.",
responses={
status.HTTP_201_CREATED: OpenApiResponse(
response=ScheduleConflictResponseSerializer,
description=(
"Signals the successful creation of the schedule and of the projected "
"timeslots."
),
),
status.HTTP_202_ACCEPTED: OpenApiResponse(
response=ScheduleDryRunResponseSerializer,
description=(
"Returns the list of timeslots that would be created, updated and deleted if "
"the schedule request would not have been sent with the dryrun flag."
),
),
status.HTTP_400_BAD_REQUEST: OpenApiResponse(
response=ErrorSerializer(many=True),
description=dedent(
"""
Returned in case the request contained invalid data.
This may happen if:
* the until date is before the start date (`no-start-after-end`),
in which case you should correct either the start or until date.
* The start and until date are the same (`no-same-day-start-and-end`).
This is only allowed for single timeslots with the recurrence rule
set to `once`. You should fix either the start or until date.
* The number of conflicts and solutions aren’t the same
(`one-solution-per-conflict`). Only one solution is allowed per conflict,
so you either offered too many or not enough solutions for any reported
conflicts.
"""
),
),
status.HTTP_403_FORBIDDEN: OpenApiResponse(
response=ErrorSerializer,
description=(
"Returned in case the request contained no or invalid authenticated data "
"or the authenticated user does not have authorization to perform the "
"requested operation."
),
),
status.HTTP_409_CONFLICT: OpenApiResponse(
response=ScheduleConflictResponseSerializer,
description=dedent(
"""
Returns the list of projected timeslots and any collisions that may have
been found for existing timeslots.
Errors on projected timeslots may include:
* 'This change on the timeslot is not allowed.'
When adding: There was a change in the schedule's data during conflict
resolution.
When updating: Fields 'start', 'end', 'byweekday' or 'rrule' have changed,
which is not allowed.
* 'No solution given': No solution was provided for the conflict in
`solutions`. Provide a value of `solution_choices`.
* 'Given solution is not accepted for this conflict.':
The solution has a value which is not part of `solution_choices`.
Provide a value of `solution_choices` (at least `ours` or `theirs`).
"""
),
),
},
),
retrieve=extend_schema(summary="Retrieve a single schedule."),
update=extend_schema(summary="Update an existing schedule."),
partial_update=extend_schema(summary="Partially update an existing schedule."),
destroy=extend_schema(summary="Delete an existing schedule."),
list=extend_schema(summary="List all schedules."),
)
class APIScheduleViewSet(
DisabledObjectPermissionCheckMixin,
NestedObjectFinderMixin,
viewsets.ModelViewSet,
):
ROUTE_FILTER_LOOKUPS = {
"show_pk": "show",
}
queryset = Schedule.objects.all()
serializer_class = ScheduleSerializer
permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
def get_queryset(self):
queryset = super().get_queryset()
def get_serializer_class(self):
if self.action in ("create", "update", "partial_update"):
return ScheduleCreateUpdateRequestSerializer
return super().get_serializer_class()
# subroute filters
show_pk = get_values(self.kwargs, "show_pk")
if show_pk:
queryset = queryset.filter(show=show_pk)
def create(self, request, *args, **kwargs):
"""
Create a schedule, generate timeslots, test for collisions and resolve them
(including notes).
return queryset
Note that creating or updating a schedule is the only way to create timeslots.
def retrieve(self, request, *args, **kwargs):
pk, show_pk = get_values(self.kwargs, "pk", "show_pk")
Only admins may add schedules.
schedule = (
get_object_or_404(Schedule, pk=pk, show=show_pk)
if show_pk
else get_object_or_404(Schedule, pk=pk)
)
The projected timeslots defined by the schedule are matched against existing
timeslots. The API will return an object that contains
serializer = ScheduleSerializer(schedule)
* the schedule's data,
* projected timeslots,
* detected collisions,
* and possible solutions.
return Response(serializer.data)
As long as no `solutions` object has been set or unresolved collisions exist,
no data is written to the database. A schedule is only created if at least
one timeslot was generated by it.
def create(self, request, *args, **kwargs):
"""
Create a schedule, generate timeslots, test for collisions and resolve them including notes
In order to resolve any possible conflicts, the client must submit a new request with
a solution for each conflict. Possible solutions are listed as part of the projected
timeslot in the `solution_choices` array. In a best-case scenario with no detected
conflicts an empty solutions object will suffice. For more details on the individual
types of solutions see the SolutionChoicesEnum.
Only superusers may add schedules.
**Please note**:
If there's more than one collision for a projected timeslot, only `theirs` and `ours`
are currently supported as solutions.
"""
if not request.user.is_superuser:
@@ -425,6 +486,10 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
# Otherwise try to resolve
resolution = Schedule.resolve_conflicts(request.data, pk, show_pk)
if all(key in resolution for key in ["create", "update", "delete"]):
# this is a dry-run
return Response(resolution, status=status.HTTP_202_ACCEPTED)
# If resolution went well
if "projected" not in resolution:
return Response(resolution, status=status.HTTP_201_CREATED)
@@ -438,24 +503,22 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
Update a schedule, generate timeslots, test for collisions and resolve
them including notes.
Only superusers may update schedules.
Only admins may update schedules.
"""
if not request.user.is_superuser:
return Response(status=status.HTTP_401_UNAUTHORIZED)
pk, show_pk = get_values(self.kwargs, "pk", "show_pk")
# Only allow updating when calling /shows/{show_pk}/schedules/{pk}/ and with the `schedule`
# JSON object
if show_pk is None or "schedule" not in request.data:
# Only allow updating when with the `schedule` JSON object
if "schedule" not in request.data:
return Response(status=status.HTTP_400_BAD_REQUEST)
schedule = self.get_object()
# If default playlist id or repetition are given, just update
if default_playlist_id := request.data.get("schedule").get(
"default_playlist_id"
):
schedule = get_object_or_404(Schedule, pk=pk, show=show_pk)
schedule.default_playlist_id = int(default_playlist_id)
schedule.save()
@@ -463,7 +526,6 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
return Response(serializer.data)
if is_repetition := request.data.get("schedule").get("is_repetition"):
schedule = get_object_or_404(Schedule, pk=pk, show=show_pk)
schedule.is_repetition = bool(is_repetition)
schedule.save()
@@ -474,11 +536,15 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
if "solutions" not in request.data:
# TODO: respond with status.HTTP_409_CONFLICT when the dashboard can handle it
return Response(
Schedule.make_conflicts(request.data["schedule"], pk, show_pk)
Schedule.make_conflicts(
request.data["schedule"], schedule.pk, schedule.show.pk
)
)
# Otherwise try to resolve
resolution = Schedule.resolve_conflicts(request.data, pk, show_pk)
resolution = Schedule.resolve_conflicts(
request.data, schedule.pk, schedule.show.pk
)
# If resolution went well
if "projected" not in resolution:
@@ -490,21 +556,13 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
def destroy(self, request, *args, **kwargs):
"""
Delete a schedule.
Only superusers may delete schedules.
Only admins may delete schedules.
"""
if not request.user.is_superuser:
return Response(status=status.HTTP_401_UNAUTHORIZED)
pk, show_pk = get_values(self.kwargs, "pk", "show_pk")
# Only allow deleting when calling /shows/{show_pk}/schedules/{pk}
if show_pk is None:
return Response(status=status.HTTP_400_BAD_REQUEST)
Schedule.objects.get(pk=pk).delete()
self.get_object().delete()
return Response(status=status.HTTP_204_NO_CONTENT)
@@ -512,21 +570,34 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
# TODO: Create is currently not implemented because timeslots are supposed to be inserted
# by creating or updating a schedule.
# There might be a use case for adding a single timeslot without any conflicts though.
@extend_schema_view(
retrieve=extend_schema(summary="Retrieve a single timeslot."),
update=extend_schema(summary="Update an existing timeslot."),
partial_update=extend_schema(summary="Partially update an existing timeslot."),
destroy=extend_schema(summary="Delete an existing timeslot."),
list=extend_schema(
summary="List all timeslots.",
description=dedent(
"""
By default, only timeslots ranging from now + 60 days will be displayed.
You may override this default overriding start and/or end parameter.
"""
),
),
)
class APITimeSlotViewSet(
DisabledObjectPermissionCheckMixin,
NestedObjectFinderMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
"""
Returns a list of timeslots.
By default, only timeslots ranging from now + 60 days will be displayed.
You may override this default overriding start and/or end parameter.
Timeslots may only be added by creating/updating a schedule.
"""
ROUTE_FILTER_LOOKUPS = {
"show_pk": "show",
"schedule_pk": "schedule",
}
permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
serializer_class = TimeSlotSerializer
@@ -534,35 +605,8 @@ class APITimeSlotViewSet(
queryset = TimeSlot.objects.all().order_by("-start")
filterset_class = filters.TimeSlotFilterSet
def get_queryset(self):
queryset = super().get_queryset()
# subroute filters
show_pk, schedule_pk = get_values(self.kwargs, "show_pk", "schedule_pk")
if show_pk:
queryset = queryset.filter(show=show_pk)
if schedule_pk:
queryset = queryset.filter(schedule=schedule_pk)
return queryset
def retrieve(self, request, *args, **kwargs):
pk, show_pk = get_values(self.kwargs, "pk", "show_pk")
if show_pk:
timeslot = get_object_or_404(TimeSlot, pk=pk, show=show_pk)
else:
timeslot = get_object_or_404(TimeSlot, pk=pk)
serializer = TimeSlotSerializer(timeslot)
return Response(serializer.data)
def update(self, request, *args, **kwargs):
"""Link a playlist_id to a timeslot"""
pk, show_pk, schedule_pk = get_values(
self.kwargs, "pk", "show_pk", "schedule_pk"
)
show_pk = get_values(self.kwargs, "show_pk")
if (
not request.user.is_superuser
@@ -570,15 +614,7 @@ class APITimeSlotViewSet(
):
return Response(status=status.HTTP_401_UNAUTHORIZED)
# Update is only allowed when calling /shows/1/schedules/1/timeslots/1 and if user owns the
# show
if schedule_pk is None or show_pk is None:
return Response(status=status.HTTP_400_BAD_REQUEST)
timeslot = get_object_or_404(
TimeSlot, pk=pk, schedule=schedule_pk, show=show_pk
)
timeslot = self.get_object()
serializer = TimeSlotSerializer(timeslot, data=request.data)
if serializer.is_valid():
serializer.save()
@@ -598,31 +634,37 @@ class APITimeSlotViewSet(
def destroy(self, request, *args, **kwargs):
"""
Deletes a timeslot.
Only superusers may delete timeslots.
Only admins may delete timeslots.
"""
if not request.user.is_superuser:
return Response(status=status.HTTP_401_UNAUTHORIZED)
pk, show_pk = get_values(self.kwargs, "pk", "show_pk")
# Only allow when calling endpoint starting with /shows/1/...
if show_pk is None:
return Response(status=status.HTTP_400_BAD_REQUEST)
TimeSlot.objects.get(pk=pk).delete()
self.get_object().delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class APINoteViewSet(viewsets.ModelViewSet):
"""
Returns a list of notes.
Superusers may access and update all notes.
"""
@extend_schema_view(
create=extend_schema(summary="Create a new note."),
retrieve=extend_schema(summary="Retrieve a single note."),
update=extend_schema(summary="Update an existing note."),
partial_update=extend_schema(
summary="Partially update an existing note.",
description="Only admins can partially update existing notes.",
),
destroy=extend_schema(summary="Delete an existing note."),
list=extend_schema(summary="List all notes."),
)
class APINoteViewSet(
DisabledObjectPermissionCheckMixin,
NestedObjectFinderMixin,
viewsets.ModelViewSet,
):
ROUTE_FILTER_LOOKUPS = {
"show_pk": "show",
"timeslot_pk": "timeslot",
}
queryset = Note.objects.all()
serializer_class = NoteSerializer
@@ -630,24 +672,11 @@ class APINoteViewSet(viewsets.ModelViewSet):
pagination_class = LimitOffsetPagination
filter_class = filters.NoteFilterSet
def get_queryset(self):
queryset = super().get_queryset()
# subroute filters
show_pk, timeslot_pk = get_values(self.kwargs, "show_pk", "timeslot_pk")
if show_pk:
queryset = queryset.filter(show=show_pk)
if timeslot_pk:
queryset = queryset.filter(timeslot=timeslot_pk)
return queryset
def create(self, request, *args, **kwargs):
"""Create a note"""
show_pk, schedule_pk, timeslot_pk = get_values(
self.kwargs, "show_pk", "schedule_pk", "timeslot_pk"
)
"""
Only admins can create new notes.
"""
show_pk, timeslot_pk = get_values(self.kwargs, "show_pk", "timeslot_pk")
if (
not request.user.is_superuser
@@ -655,10 +684,6 @@ class APINoteViewSet(viewsets.ModelViewSet):
):
return Response(status=status.HTTP_401_UNAUTHORIZED)
# Only create a note if show_id, timeslot_id and schedule_id is given
if show_pk is None or schedule_pk is None or timeslot_pk is None:
return Response(status=status.HTTP_400_BAD_REQUEST)
serializer = NoteSerializer(
data={"show": show_pk, "timeslot": timeslot_pk} | request.data,
context={"user_id": request.user.id},
@@ -676,52 +701,11 @@ class APINoteViewSet(viewsets.ModelViewSet):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def retrieve(self, request, *args, **kwargs):
def update(self, request, *args, **kwargs):
"""
Returns a single note
Called by:
/notes/1
/shows/1/notes/1
/shows/1/timeslots/1/note/1
/shows/1/schedules/1/timeslots/1/note/1
Only admins can update existing notes.
"""
pk, show_pk, schedule_pk, timeslot_pk = get_values(
self.kwargs, "pk", "show_pk", "schedule_pk", "timeslot_pk"
)
#
# /shows/1/notes/1
#
# Returns a note to a show
#
if show_pk and timeslot_pk is None and schedule_pk is None:
note = get_object_or_404(Note, pk=pk, show=show_pk)
#
# /shows/1/timeslots/1/note/1
# /shows/1/schedules/1/timeslots/1/note/1
#
# Return a note to a timeslot
#
elif show_pk and timeslot_pk:
note = get_object_or_404(Note, pk=pk, show=show_pk, timeslot=timeslot_pk)
#
# /notes/1
#
# Returns the given note
#
else:
note = get_object_or_404(Note, pk=pk)
serializer = NoteSerializer(note)
return Response(serializer.data)
def update(self, request, *args, **kwargs):
pk, show_pk, schedule_pk, timeslot_pk = get_values(
self.kwargs, "pk", "show_pk", "schedule_pk", "timeslot_pk"
)
show_pk = get_values(self.kwargs, "show_pk")
if (
not request.user.is_superuser
@@ -729,13 +713,7 @@ class APINoteViewSet(viewsets.ModelViewSet):
):
return Response(status=status.HTTP_401_UNAUTHORIZED)
# Allow PUT only when calling
# /shows/{show_pk}/schedules/{schedule_pk}/timeslots/{timeslot_pk}/note/{pk}
if show_pk is None or schedule_pk is None or timeslot_pk is None:
return Response(status=status.HTTP_400_BAD_REQUEST)
note = get_object_or_404(Note, pk=pk, timeslot=timeslot_pk, show=show_pk)
note = self.get_object()
serializer = NoteSerializer(note, data=request.data)
if serializer.is_valid():
@@ -754,9 +732,10 @@ class APINoteViewSet(viewsets.ModelViewSet):
return Response(status=status.HTTP_400_BAD_REQUEST)
def destroy(self, request, *args, **kwargs):
pk, show_pk, schedule_pk, timeslot_pk = get_values(
self.kwargs, "pk", "show_pk", "schedule_pk", "timeslot_pk"
)
"""
Only admins can delete existing notes.
"""
show_pk = get_values(self.kwargs, "show_pk")
if (
not request.user.is_superuser
@@ -764,10 +743,7 @@ class APINoteViewSet(viewsets.ModelViewSet):
):
return Response(status=status.HTTP_401_UNAUTHORIZED)
if pk is None or show_pk is None or schedule_pk is None or timeslot_pk is None:
return Response(status=status.HTTP_400_BAD_REQUEST)
Note.objects.get(pk=pk).delete()
self.get_object().delete()
return Response(status=status.HTTP_204_NO_CONTENT)
@@ -776,65 +752,95 @@ class ActiveFilterMixin:
filter_class = filters.ActiveFilterSet
@extend_schema_view(
create=extend_schema(summary="Create a new category."),
retrieve=extend_schema(summary="Retrieve a single category."),
update=extend_schema(summary="Update an existing category."),
partial_update=extend_schema(summary="Partially update an existing category."),
destroy=extend_schema(summary="Delete an existing category."),
list=extend_schema(summary="List all categories."),
)
class APICategoryViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
"""
Returns a list of categories.
"""
queryset = Category.objects.all()
serializer_class = CategorySerializer
@extend_schema_view(
create=extend_schema(summary="Create a new type."),
retrieve=extend_schema(summary="Retrieve a single type."),
update=extend_schema(summary="Update an existing type."),
partial_update=extend_schema(summary="Partially update an existing type."),
destroy=extend_schema(summary="Delete an existing type."),
list=extend_schema(summary="List all types."),
)
class APITypeViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
"""
Returns a list of types.
"""
queryset = Type.objects.all()
serializer_class = TypeSerializer
@extend_schema_view(
create=extend_schema(summary="Create a new topic."),
retrieve=extend_schema(summary="Retrieve a single topic."),
update=extend_schema(summary="Update an existing topic."),
partial_update=extend_schema(summary="Partially update an existing topic."),
destroy=extend_schema(summary="Delete an existing topic."),
list=extend_schema(summary="List all topics."),
)
class APITopicViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
"""
Returns a list of topics.
"""
queryset = Topic.objects.all()
serializer_class = TopicSerializer
@extend_schema_view(
create=extend_schema(summary="Create a new music focus."),
retrieve=extend_schema(summary="Retrieve a single music focus."),
update=extend_schema(summary="Update an existing music focus."),
partial_update=extend_schema(summary="Partially update an existing music focus."),
destroy=extend_schema(summary="Delete an existing music focus."),
list=extend_schema(summary="List all music focuses."),
)
class APIMusicFocusViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
"""
Returns a list of music focuses.
"""
queryset = MusicFocus.objects.all()
serializer_class = MusicFocusSerializer
@extend_schema_view(
create=extend_schema(summary="Create a new funding category."),
retrieve=extend_schema(summary="Retrieve a single funding category."),
update=extend_schema(summary="Update an existing funding category."),
partial_update=extend_schema(
summary="Partially update an existing funding category."
),
destroy=extend_schema(summary="Delete an existing funding category."),
list=extend_schema(summary="List all funding categories."),
)
class APIFundingCategoryViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
"""
Returns a list of funding categories.
"""
queryset = FundingCategory.objects.all()
serializer_class = FundingCategorySerializer
@extend_schema_view(
create=extend_schema(summary="Create a new language."),
retrieve=extend_schema(summary="Retrieve a single language."),
update=extend_schema(summary="Update an existing language."),
partial_update=extend_schema(summary="Partially update an existing language."),
destroy=extend_schema(summary="Delete an existing language."),
list=extend_schema(summary="List all languages."),
)
class APILanguageViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
"""
Returns a list of languages.
"""
queryset = Language.objects.all()
serializer_class = LanguageSerializer
@extend_schema_view(
create=extend_schema(summary="Create a new host."),
retrieve=extend_schema(summary="Retrieve a single host."),
update=extend_schema(summary="Update an existing host."),
partial_update=extend_schema(summary="Partially update an existing host."),
destroy=extend_schema(summary="Delete an existing host."),
list=extend_schema(summary="List all hosts."),
)
class APIHostViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
"""
Returns a list of hosts.
"""
queryset = Host.objects.all()
serializer_class = HostSerializer
pagination_class = LimitOffsetPagination
Loading