如何为序列化器方法结果创建json属性

时间:2019-01-01 18:53:25

标签: python django-models django-rest-framework django-serializer

在这里,我有一个序列化器,该序列化器是一个get_is_liked方法。 此方法将返回一个布尔值,无论当前用户是否喜欢该帖子。

现在,我想像其他字段一样以json属性的格式获取此方法的结果。 假设有一个移动应用程序向登录用户发送请求,以显示该帖子是否曾经喜欢过。

serializer.py

class BookSerializer(serializers.ModelSerializer):
    user = serializers.SlugRelatedField(slug_field='username', read_only=True)

    class Meta:
        fields = (
            'id',
            'name',
            'description',
            'user',
            'likes'
        )
        model = models.Book

    def get_is_liked(self, obj):
        requestUser = self.context['request'].user
        return models.BookLike.objects.filter(
            book=obj, 
            liker=requestUser
        ).exists()

views.py

class ListBookView(generics.ListCreateAPIView):
    permission_classes = (IsAuthenticated, )
    queryset = models.Book.objects.all()
    serializer_class = serializers.BookSerializer

    def perform_create(self, serializer):
       serializer.save(user=self.request.user)

class DetailBookView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = (IsOwnerOrReadOnly, )
    queryset = models.Book.objects.all()
    serializer_class = serializers.BookSerializer

已编辑:

models.py

class Book(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=125)
    description = models.CharField(max_length=225)
    likes = models.PositiveIntegerField(default=0)

def __str__(self):
   return self.name


class BookLike(models.Model):
    book = models.ForeignKey(Book, on_delete=models.CASCADE)
    liker = models.ForeignKey(User, on_delete=models.CASCADE)

def __str__(self):
   return 'Post: {0}, Liked by {1}'.format(self.book, self.liker)

现在,我不知道该怎么做?!

1 个答案:

答案 0 :(得分:1)

使用SerializerMethodField创建一个只读字段,并通过调用序列化程序类上的方法获取其值。

在串行器上创建一个名为get_<field_name>的字段和方法。此方法有两个参数:self和要序列化的给定对象(就像您的get_is_liked()方法一样)

serializers.py

class BookSerializer(serializers.ModelSerializer):
    is_liked = serializers.SerializerMethodField()
    likes = serializers.SerializerMethodField()    

    def get_is_liked(self, obj):
        '''
        Returns a boolean that represents whether the book has 
        already been liked by the user
        '''
        return models.BookLike.objects.filter(
            book=obj, 
            liker=self.context['request'].user
        ).exists()

   def get_likes(self, obj):
       '''
       Returns the numer of likes of the book
       '''
       return models.BookLike.objects.filter(book=obj).count()

实现喜欢的终点

get_is_liked()将始终返回False,直到API允许用户提供喜欢的书为止。为此,必须创建一个新的端点。

我建议您将视图合并到单个视图集中,并添加额外的操作(请参见documentation)以执行类似的功能。像这样:

from rest_framework.decorators import action


class BookViewSet(generics.ModelViewSet):
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
    queryset = models.Book.objects.all()
    serializer_class = serializers.BookSerializer

    @action(methods=['post'], detail=True)
    def like(self, request, pk=None):
        book = self.get_object()

        # Create a like for the book or get an existent one
        like, created = BookLike.objects.get_or_create(
            book=book, 
            liker=request.user
        )

        # User never gave a like for this book
        if created:
            return Response({
                'detail': 'Your like was registered with success.'
            })

        # Book already liked by the user (dislike or error?)
        return Response({
            'detail': 'Only one like per book is allowed.'
        }, status.HTTP_400_BAD_REQUEST)

urls.py

from rest_framework.routers import DefaultRouter

from django.urls import path, include

from . import views


router = DefaultRouter()
router.register(r'books', views.BookViewSet, base_name='book')

urlpatterns = [
    path('', include(router.urls)),
]

在此配置下,like的终结点为

POST /books/{bookId}/like/