diff --git a/api/mixins.py b/api/mixins.py deleted file mode 100644 index c5266da88..000000000 --- a/api/mixins.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -from collections import OrderedDict - -from rest_framework.response import Response - - -class TimerFieldSupportMixin: - def options(self, request, *args, **kwargs): - """ - Add information about the optional "timer" field. - """ - meta = self.metadata_class() - data = meta.determine_metadata(request, self) - post = data.get("actions").get("POST") # type: OrderedDict - post["timer"] = OrderedDict( - { - "type": "integer", - "required": False, - "read_only": False, - "label": "Timer", - "details": "ID for an existing Timer, may be used in place of the " - "`start`, `end`, and/or `child` fields. ", - } - ) - details = "Required unless a value is provided in the `timer` field." - post["child"]["details"] = details - post["start"]["details"] = details - post["end"]["details"] = details - return Response(data) diff --git a/api/serializers.py b/api/serializers.py index 9c3314fc7..19013ea4b 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -39,16 +39,30 @@ class CoreModelWithDurationSerializer(CoreModelSerializer): child = serializers.PrimaryKeyRelatedField( allow_null=True, - allow_empty=True, + help_text="Required unless a Timer value is provided.", queryset=models.Child.objects.all(), required=False, ) + timer = serializers.PrimaryKeyRelatedField( + allow_null=True, + help_text="May be used in place of the Start, End, and/or Child values.", + queryset=models.Timer.objects.all(), + required=False, + write_only=True, + ) + class Meta: abstract = True extra_kwargs = { - "start": {"required": False}, - "end": {"required": False}, + "start": { + "help_text": "Required unless a Timer value is provided.", + "required": False, + }, + "end": { + "help_text": "Required unless a Timer value is provided.", + "required": False, + }, } def validate(self, attrs): @@ -56,11 +70,12 @@ def validate(self, attrs): # of "start" and "end" fields as well as "child" if it is set on the # Timer entry. timer = None - if "timer" in self.initial_data: - try: - timer = models.Timer.objects.get(pk=self.initial_data["timer"]) - except models.Timer.DoesNotExist: - raise ValidationError({"timer": ["Timer does not exist."]}) + if "timer" in attrs: + # Remove the "timer" attribute (super validation would fail as it + # is not a true field on the model). + timer = attrs["timer"] + attrs.pop("timer") + if timer.end: end = timer.end else: @@ -142,6 +157,7 @@ class Meta(CoreModelWithDurationSerializer.Meta): "child", "start", "end", + "timer", "duration", "type", "method", @@ -172,7 +188,17 @@ class Meta: class SleepSerializer(CoreModelWithDurationSerializer, TaggableSerializer): class Meta(CoreModelWithDurationSerializer.Meta): model = models.Sleep - fields = ("id", "child", "start", "end", "duration", "nap", "notes", "tags") + fields = ( + "id", + "child", + "start", + "end", + "timer", + "duration", + "nap", + "notes", + "tags", + ) class TagSerializer(serializers.HyperlinkedModelSerializer): @@ -220,7 +246,16 @@ def validate(self, attrs): class TummyTimeSerializer(CoreModelWithDurationSerializer, TaggableSerializer): class Meta(CoreModelWithDurationSerializer.Meta): model = models.TummyTime - fields = ("id", "child", "start", "end", "duration", "milestone", "tags") + fields = ( + "id", + "child", + "start", + "end", + "timer", + "duration", + "milestone", + "tags", + ) class UserSerializer(serializers.ModelSerializer): diff --git a/api/views.py b/api/views.py index 80e5e28ea..3a7964cf0 100644 --- a/api/views.py +++ b/api/views.py @@ -6,7 +6,6 @@ from core import models from . import serializers, filters -from .mixins import TimerFieldSupportMixin class BMIViewSet(viewsets.ModelViewSet): @@ -37,7 +36,7 @@ class DiaperChangeViewSet(viewsets.ModelViewSet): filterset_class = filters.DiaperChangeFilter -class FeedingViewSet(TimerFieldSupportMixin, viewsets.ModelViewSet): +class FeedingViewSet(viewsets.ModelViewSet): queryset = models.Feeding.objects.all() serializer_class = serializers.FeedingSerializer filterset_class = filters.FeedingFilter @@ -67,7 +66,7 @@ class PumpingViewSet(viewsets.ModelViewSet): filterset_class = filters.PumpingFilter -class SleepViewSet(TimerFieldSupportMixin, viewsets.ModelViewSet): +class SleepViewSet(viewsets.ModelViewSet): queryset = models.Sleep.objects.all() serializer_class = serializers.SleepSerializer filterset_class = filters.SleepFilter @@ -104,7 +103,7 @@ def restart(self, request, pk=None): return Response(self.serializer_class(timer).data) -class TummyTimeViewSet(TimerFieldSupportMixin, viewsets.ModelViewSet): +class TummyTimeViewSet(viewsets.ModelViewSet): queryset = models.TummyTime.objects.all() serializer_class = serializers.TummyTimeSerializer filterset_class = filters.TummyTimeFilter diff --git a/openapi-schema.yml b/openapi-schema.yml index b3fb719c3..fdd255997 100644 --- a/openapi-schema.yml +++ b/openapi-schema.yml @@ -4684,12 +4684,20 @@ components: child: type: integer nullable: true + description: Required unless a Timer value is provided. start: type: string format: date-time + description: Required unless a Timer value is provided. end: type: string format: date-time + description: Required unless a Timer value is provided. + timer: + type: integer + writeOnly: true + nullable: true + description: May be used in place of the Start, End, and/or Child values. duration: type: string readOnly: true @@ -4809,12 +4817,20 @@ components: child: type: integer nullable: true + description: Required unless a Timer value is provided. start: type: string format: date-time + description: Required unless a Timer value is provided. end: type: string format: date-time + description: Required unless a Timer value is provided. + timer: + type: integer + writeOnly: true + nullable: true + description: May be used in place of the Start, End, and/or Child values. duration: type: string readOnly: true @@ -4905,12 +4921,20 @@ components: child: type: integer nullable: true + description: Required unless a Timer value is provided. start: type: string format: date-time + description: Required unless a Timer value is provided. end: type: string format: date-time + description: Required unless a Timer value is provided. + timer: + type: integer + writeOnly: true + nullable: true + description: May be used in place of the Start, End, and/or Child values. duration: type: string readOnly: true