diff --git a/backend/server/adventures/models.py b/backend/server/adventures/models.py index 3195c04..4f8109d 100644 --- a/backend/server/adventures/models.py +++ b/backend/server/adventures/models.py @@ -38,14 +38,13 @@ class Adventure(models.Model): def clean(self): if self.trip: - if self.is_public and not self.trip.is_public: - raise ValidationError('Public adventures must be associated with a public trip') if self.trip.is_public and not self.is_public: raise ValidationError('Adventures associated with a public trip must be public') if self.user_id != self.trip.user_id: raise ValidationError('Adventures must be associated with trips owned by the same user') - elif self.is_public: - raise ValidationError('Public adventures must be associated with a trip') + + def __str__(self): + return self.name class Trip(models.Model): id = models.AutoField(primary_key=True) @@ -57,10 +56,15 @@ class Trip(models.Model): date = models.DateField(blank=True, null=True) is_public = models.BooleanField(default=False) + # if connected adventures are private and trip is public, raise an error def clean(self): - if self.is_public: - if self.adventures.filter(is_public=False).exists(): - raise ValidationError('Public trips cannot have private adventures') + if self.is_public: + for adventure in self.adventure_set.all(): + if not adventure.is_public: + raise ValidationError('Public trips cannot be associated with private adventures') + + def __str__(self): return self.name + diff --git a/backend/server/adventures/permissions.py b/backend/server/adventures/permissions.py new file mode 100644 index 0000000..f4dfbea --- /dev/null +++ b/backend/server/adventures/permissions.py @@ -0,0 +1,15 @@ +from rest_framework import permissions + +class IsOwnerOrReadOnly(permissions.BasePermission): + """ + Custom permission to only allow owners of an object to edit it. + """ + + def has_object_permission(self, request, view, obj): + # Read permissions are allowed to any request, + # so we'll always allow GET, HEAD or OPTIONS requests. + if request.method in permissions.SAFE_METHODS: + return True + + # Write permissions are only allowed to the owner of the object. + return obj.user_id == request.user \ No newline at end of file diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index c987681..69daebd 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -1,5 +1,5 @@ import os -from .models import Adventure +from .models import Adventure, Trip from rest_framework import serializers class AdventureSerializer(serializers.ModelSerializer): @@ -16,4 +16,12 @@ class AdventureSerializer(serializers.ModelSerializer): # remove any ' from the url public_url = public_url.replace("'", "") representation['image'] = f"{public_url}/media/{instance.image.name}" - return representation \ No newline at end of file + return representation + +class TripSerializer(serializers.ModelSerializer): + + class Meta: + model = Trip + fields = '__all__' # Serialize all fields of the Adventure model + + \ No newline at end of file diff --git a/backend/server/adventures/urls.py b/backend/server/adventures/urls.py index 375e251..6c4b962 100644 --- a/backend/server/adventures/urls.py +++ b/backend/server/adventures/urls.py @@ -1,9 +1,10 @@ from django.urls import include, path from rest_framework.routers import DefaultRouter -from .views import AdventureViewSet +from .views import AdventureViewSet, TripViewSet router = DefaultRouter() router.register(r'adventures', AdventureViewSet, basename='adventures') +router.register(r'trips', TripViewSet, basename='trips') urlpatterns = [ # Include the router under the 'api/' prefix diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 057e60f..b0f1282 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -1,19 +1,17 @@ from rest_framework.decorators import action from rest_framework import viewsets from rest_framework.response import Response -from .models import Adventure -from .serializers import AdventureSerializer +from .models import Adventure, Trip +from .serializers import AdventureSerializer, TripSerializer from rest_framework.permissions import IsAuthenticated from django.db.models import Q - -# Create your views here. +from .permissions import IsOwnerOrReadOnly class AdventureViewSet(viewsets.ModelViewSet): serializer_class = AdventureSerializer - permission_classes = [IsAuthenticated] + permission_classes = [IsAuthenticated, IsOwnerOrReadOnly] def get_queryset(self): - # Allow any user to see public adventures or their own adventures return Adventure.objects.filter( Q(is_public=True) | Q(user_id=self.request.user.id) ) @@ -21,24 +19,56 @@ class AdventureViewSet(viewsets.ModelViewSet): def perform_create(self, serializer): serializer.save(user_id=self.request.user) - # Custom actions to return visited and planned adventures @action(detail=False, methods=['get']) def visited(self, request): visited_adventures = Adventure.objects.filter( - type='visited', user_id=request.user.id, trip_id=None) + type='visited', user_id=request.user.id, trip=None) serializer = self.get_serializer(visited_adventures, many=True) return Response(serializer.data) @action(detail=False, methods=['get']) def planned(self, request): planned_adventures = Adventure.objects.filter( - type='planned', user_id=request.user.id, trip_id=None) + type='planned', user_id=request.user.id, trip=None) serializer = self.get_serializer(planned_adventures, many=True) return Response(serializer.data) @action(detail=False, methods=['get']) def featured(self, request): featured_adventures = Adventure.objects.filter( - type='featured', is_public=True, trip_id=None) + type='featured', is_public=True, trip=None) serializer = self.get_serializer(featured_adventures, many=True) + return Response(serializer.data) + +class TripViewSet(viewsets.ModelViewSet): + serializer_class = TripSerializer + permission_classes = [IsAuthenticated, IsOwnerOrReadOnly] + + def get_queryset(self): + return Trip.objects.filter( + Q(is_public=True) | Q(user_id=self.request.user.id) + ) + + def perform_create(self, serializer): + serializer.save(user_id=self.request.user) + + @action(detail=False, methods=['get']) + def visited(self, request): + trips = Trip.objects.filter( + type='visited', user_id=request.user.id) + serializer = self.get_serializer(trips, many=True) + return Response(serializer.data) + + @action(detail=False, methods=['get']) + def planned(self, request): + trips = Trip.objects.filter( + type='planned', user_id=request.user.id) + serializer = self.get_serializer(trips, many=True) + return Response(serializer.data) + + @action(detail=False, methods=['get']) + def featured(self, request): + trips = Trip.objects.filter( + type='featured', is_public=True) + serializer = self.get_serializer(trips, many=True) return Response(serializer.data) \ No newline at end of file