diff --git a/core/models.py b/core/models.py index a61c822..0568e15 100644 --- a/core/models.py +++ b/core/models.py @@ -83,6 +83,7 @@ class Board(models.Model): class Pin(models.Model): submitter = models.ForeignKey(User) + private = models.BooleanField(default=False, blank=False) url = models.CharField(null=True, blank=True, max_length=256) referer = models.CharField(null=True, blank=True, max_length=256) description = models.TextField(blank=True, null=True) diff --git a/core/permissions.py b/core/permissions.py index 0df1293..b46750c 100644 --- a/core/permissions.py +++ b/core/permissions.py @@ -21,6 +21,19 @@ class IsOwnerOrReadOnly(permissions.IsAuthenticatedOrReadOnly): return getattr(obj, self.__owner_field_name) == request.user +class OwnerOnlyIfPrivate(permissions.BasePermission): + def __init__(self, owner_field_name="owner"): + self.__owner_field_name = owner_field_name + + def __call__(self): + return self + + def has_object_permission(self, request, view, obj): + if getattr(obj, "private"): + return request.user == getattr(obj, self.__owner_field_name) + return True + + class OwnerOnly(permissions.IsAuthenticatedOrReadOnly): def has_permission(self, request, view): diff --git a/core/serializers.py b/core/serializers.py index 6cef857..acfd1dd 100644 --- a/core/serializers.py +++ b/core/serializers.py @@ -1,4 +1,5 @@ from django.conf import settings +from django.db.models import Q from rest_framework import serializers from rest_framework.exceptions import ValidationError from taggit.models import Tag @@ -9,6 +10,14 @@ from django_images.models import Thumbnail from users.serializers import UserSerializer +def filter_private_pin(request, query): + if request.user.is_authenticated: + query = query.exclude(~Q(submitter=request.user), private=True) + else: + query = query.exclude(private=True) + return query.select_related('image', 'submitter') + + class ThumbnailSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Thumbnail @@ -164,7 +173,9 @@ class BoardSerializer(serializers.HyperlinkedModelSerializer): } submitter = UserSerializer(read_only=True) - pins_detail = PinSerializer(source="pins", many=True, read_only=True) + pins_detail = serializers.SerializerMethodField( + read_only=True, + ) pins = serializers.HyperlinkedRelatedField( write_only=True, queryset=Pin.objects.all(), @@ -187,6 +198,12 @@ class BoardSerializer(serializers.HyperlinkedModelSerializer): help_text="only patch method works for this field" ) + def get_pins_detail(self, instance): + query = instance.pins.all() + request = self.context['request'] + query = filter_private_pin(request, query) + return [PinSerializer(pin, context=self.context).data for pin in query] + @staticmethod def _get_list(pins_id): return tuple(Pin.objects.filter(id__in=pins_id)) diff --git a/core/views.py b/core/views.py index 543486b..e97a0a2 100644 --- a/core/views.py +++ b/core/views.py @@ -1,3 +1,4 @@ +from django.db.models import Q from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_page from django_filters.rest_framework import DjangoFilterBackend @@ -8,7 +9,8 @@ from taggit.models import Tag from core import serializers as api from core.models import Image, Pin, Board -from core.permissions import IsOwnerOrReadOnly +from core.permissions import IsOwnerOrReadOnly, OwnerOnlyIfPrivate +from core.serializers import filter_private_pin class ImageViewSet(mixins.CreateModelMixin, GenericViewSet): @@ -20,13 +22,17 @@ class ImageViewSet(mixins.CreateModelMixin, GenericViewSet): class PinViewSet(viewsets.ModelViewSet): - queryset = Pin.objects.all().select_related('image', 'submitter') serializer_class = api.PinSerializer filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter) filter_fields = ("submitter__username", 'tags__name', ) ordering_fields = ('-id', ) ordering = ('-id', ) - permission_classes = [IsOwnerOrReadOnly("submitter"), ] + permission_classes = [IsOwnerOrReadOnly("submitter"), OwnerOnlyIfPrivate("submitter")] + + def get_queryset(self): + query = Pin.objects.all() + request = self.request + return filter_private_pin(request, query) class BoardViewSet(viewsets.ModelViewSet): @@ -67,7 +73,7 @@ class TagAutoCompleteViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): drf_router = routers.DefaultRouter() -drf_router.register(r'pins', PinViewSet) +drf_router.register(r'pins', PinViewSet, basename="pin") drf_router.register(r'images', ImageViewSet) drf_router.register(r'boards', BoardViewSet) drf_router.register(r'tags-auto-complete', TagAutoCompleteViewSet)