From c1332ee1f0aeeeb409b493b0345998c9590a4b55 Mon Sep 17 00:00:00 2001 From: moanos Date: Sun, 24 Nov 2024 22:29:19 +0100 Subject: [PATCH] feat: Add API methods for Animals, Images, Species andRescue orgs --- src/fellchensammlung/api/serializers.py | 46 ++++++++- src/fellchensammlung/api/urls.py | 12 ++- src/fellchensammlung/api/views.py | 123 ++++++++++++++++++++++-- 3 files changed, 169 insertions(+), 12 deletions(-) diff --git a/src/fellchensammlung/api/serializers.py b/src/fellchensammlung/api/serializers.py index c6d0672..2c11099 100644 --- a/src/fellchensammlung/api/serializers.py +++ b/src/fellchensammlung/api/serializers.py @@ -1,10 +1,48 @@ -from ..models import AdoptionNotice +from ..models import Animal, RescueOrganization, AdoptionNotice, Species, Image from rest_framework import serializers - - class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = AdoptionNotice - fields = ['created_at', 'last_checked', "searching_since", "name", "description", "further_information", "group_only"] + fields = ['created_at', 'last_checked', "searching_since", "name", "description", "further_information", + "group_only"] + + +class AnimalCreateSerializer(serializers.ModelSerializer): + class Meta: + model = Animal + fields = ["name", "date_of_birth", "description", "species", "sex", "adoption_notice"] + + +class AnimalGetSerializer(serializers.ModelSerializer): + class Meta: + model = Animal + fields = "__all__" + + +class RescueOrganizationSerializer(serializers.ModelSerializer): + class Meta: + model = RescueOrganization + exclude = ["internal_comment", "allows_using_materials"] + + +class ImageCreateSerializer(serializers.ModelSerializer): + @staticmethod + def _animal_or_an(value): + if not value in ["animal", "adoption_notice"]: + raise serializers.ValidationError( + 'Set either animal or adoption_notice, depending on what type of object the image should be attached to.') + + attach_to_type = serializers.CharField(validators=[_animal_or_an]) + attach_to = serializers.IntegerField() + + class Meta: + model = Image + exclude = ["owner"] + + +class SpeciesSerializer(serializers.ModelSerializer): + class Meta: + model = Species + fields = "__all__" diff --git a/src/fellchensammlung/api/urls.py b/src/fellchensammlung/api/urls.py index 553dd2f..cb66385 100644 --- a/src/fellchensammlung/api/urls.py +++ b/src/fellchensammlung/api/urls.py @@ -1,8 +1,16 @@ from django.urls import path from .views import ( - AdoptionNoticeApiView + AdoptionNoticeApiView, + AnimalApiView, RescueOrganizationApiView, AddImageApiView, SpeciesApiView ) urlpatterns = [ - path('adoption_notice', AdoptionNoticeApiView.as_view()), + path("adoption_notice", AdoptionNoticeApiView.as_view(), name="api-adoption-notice-list"), + path("adoption_notice//", AdoptionNoticeApiView.as_view(), name="api-adoption-notice-detail"), + path("animals/", AnimalApiView.as_view(), name="api-animal-list"), + path("animals//", AnimalApiView.as_view(), name="api-animal-detail"), + path("organizations/", RescueOrganizationApiView.as_view(), name="api-organization-list"), + path("organizations//", RescueOrganizationApiView.as_view(), name="api-organization-detail"), + path("images/", AddImageApiView.as_view(), name="api-add-image"), + path("species/", SpeciesApiView.as_view(), name="api-species-list"), ] diff --git a/src/fellchensammlung/api/views.py b/src/fellchensammlung/api/views.py index 83acdc3..e89c3dd 100644 --- a/src/fellchensammlung/api/views.py +++ b/src/fellchensammlung/api/views.py @@ -7,18 +7,36 @@ from rest_framework.permissions import IsAuthenticated from django.db import transaction from fellchensammlung.models import AdoptionNotice, Animal, Log, TrustLevel from fellchensammlung.tasks import add_adoption_notice_location -from .serializers import AdoptionNoticeSerializer +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from .serializers import ( + AnimalGetSerializer, + AnimalCreateSerializer, + RescueOrganizationSerializer, + AdoptionNoticeSerializer, + ImageCreateSerializer, + SpeciesSerializer, +) +from fellchensammlung.models import Animal, RescueOrganization, AdoptionNotice, Species, Image class AdoptionNoticeApiView(APIView): - permission_classes = [permissions.IsAuthenticated] + permission_classes = [IsAuthenticated] def get(self, request, *args, **kwargs): - serializer_context = { - 'request': request, - } + """ + Retrieve adoption notices with their related animals and images. + """ + adoption_notice_id = kwargs.get("id") + if adoption_notice_id: + try: + adoption_notice = AdoptionNotice.objects.get(pk=adoption_notice_id) + serializer = AdoptionNoticeSerializer(adoption_notice, context={"request": request}) + return Response(serializer.data, status=status.HTTP_200_OK) + except AdoptionNotice.DoesNotExist: + return Response({"error": "Adoption notice not found."}, status=status.HTTP_404_NOT_FOUND) adoption_notices = AdoptionNotice.objects.all() - serializer = AdoptionNoticeSerializer(adoption_notices, many=True, context=serializer_context) + serializer = AdoptionNoticeSerializer(adoption_notices, many=True, context={"request": request}) return Response(serializer.data, status=status.HTTP_200_OK) @transaction.atomic @@ -53,3 +71,96 @@ class AdoptionNoticeApiView(APIView): {"message": "Adoption notice created successfully!", "id": adoption_notice.pk}, status=status.HTTP_201_CREATED, ) + + +class AnimalApiView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, *args, **kwargs): + """ + Get list of animals or a specific animal by ID. + """ + animal_id = kwargs.get("id") + if animal_id: + try: + animal = Animal.objects.get(pk=animal_id) + serializer = AnimalGetSerializer(animal, context={"request": request}) + return Response(serializer.data, status=status.HTTP_200_OK) + except Animal.DoesNotExist: + return Response({"error": "Animal not found."}, status=status.HTTP_404_NOT_FOUND) + animals = Animal.objects.all() + serializer = AnimalGetSerializer(animals, many=True, context={"request": request}) + return Response(serializer.data, status=status.HTTP_200_OK) + + @transaction.atomic + def post(self, request, *args, **kwargs): + """ + Create a new animal. + """ + serializer = AnimalCreateSerializer(data=request.data, context={"request": request}) + if serializer.is_valid(): + animal = serializer.save(owner=request.user) + return Response( + {"message": "Animal created successfully!", "id": animal.id}, + status=status.HTTP_201_CREATED, + ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class RescueOrganizationApiView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, *args, **kwargs): + """ + Get list of rescue organizations or a specific organization by ID. + """ + org_id = kwargs.get("id") + if org_id: + try: + organization = RescueOrganization.objects.get(pk=org_id) + serializer = RescueOrganizationSerializer(organization, context={"request": request}) + return Response(serializer.data, status=status.HTTP_200_OK) + except RescueOrganization.DoesNotExist: + return Response({"error": "Organization not found."}, status=status.HTTP_404_NOT_FOUND) + organizations = RescueOrganization.objects.all() + serializer = RescueOrganizationSerializer(organizations, many=True, context={"request": request}) + return Response(serializer.data, status=status.HTTP_200_OK) + + +class AddImageApiView(APIView): + permission_classes = [IsAuthenticated] + + @transaction.atomic + def post(self, request, *args, **kwargs): + """ + Add an image to an animal or adoption notice. + """ + serializer = ImageCreateSerializer(data=request.data, context={"request": request}) + if serializer.is_valid(): + if serializer.validated_data["attach_to_type"] == "animal": + object_to_attach_to = Animal.objects.get(id=serializer.validated_data["attach_to"]) + elif serializer.fields["attach_to_type"] == "adoption_notice": + object_to_attach_to = AdoptionNotice.objects.get(id=serializer.validated_data["attach_to"]) + else: + raise ValueError("Unknown attach_to_type given, should not happen. Check serializer") + serializer.validated_data.pop('attach_to_type', None) + serializer.validated_data.pop('attach_to', None) + image = serializer.save(owner=request.user) + object_to_attach_to.photos.add(image) + return Response( + {"message": "Image added successfully!", "id": image.id}, + status=status.HTTP_201_CREATED, + ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class SpeciesApiView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, *args, **kwargs): + """ + Retrieve a list of species. + """ + species = Species.objects.all() + serializer = SpeciesSerializer(species, many=True, context={"request": request}) + return Response(serializer.data, status=status.HTTP_200_OK)