DRF добавляет вычисляемое поле на основе запрашивающего пользователя
У меня есть модель, которая имеет определенные многие-многие поля для пользовательской модели. Теперь, чтобы предотвратить утечку информации, я не хочу возвращать все связанное поле через остальной фреймворк. Но я хочу создать некое вычисляемое поле, которое будет возвращать True, если запрашивающий пользователь находится в связанном поле, и False в противном случае. Есть ли способ заставить это работать?
Например, в текущем виде rest framework будет перечислять пользователей по "user_like" и "user_bookmark", чего я не хочу, поэтому я хочу исключить их из сериализации. Но я хочу иметь поле, скажем, с именем is_liked, которое будет истинным, если request.user находится в user_like, и ложным в противном случае.
Моя текущая установка:
модель
class Post(models.Model):
title = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ImageField(upload_to='dream_photos')
description = models.TextField(max_length=500)
date_added = models.DateTimeField(auto_now_add=True)
user_like = models.ManyToManyField(User, related_name='likes', blank=True)
user_bookmark = models.ManyToManyField(
User, related_name='bookmarks', blank=True)
total_likes = models.PositiveIntegerField(db_index=True, default=0)
tags = TaggableManager()
сериализатор
class PostSerializer(TaggitSerializer, serializers.ModelSerializer):
tags = TagListSerializerField()
class Meta:
model = Dream
fields = ('title','user', 'image','description','date_added', 'tags', 'total_likes' )
просмотров
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.prefetch_related('user').all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated]
@action(detail=False, methods=['get'], url_path='current-profile', url_name='current-profile')
def current_user_posts(self, request):
# I expected this to add the extra field I required
# But it does not seem to work as expected
queryset = self.get_queryset().filter(user=request.user).annotate(
bookmark=(request.user in "user_bookmark"))
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Ожидаемое поведение при запросе:
{
"id": 1,
"tags": [
"test"
],
"title": "Tets",
"image": "http://127.0.0.1:8000/media/post_photos/photo1648638314.jpeg",
"description": "TEst",
"date_added": "2022-05-20T17:47:55.739431Z",
"total_likes": 0,
"user": 1,
"like": true, // true if current user is in user_like, false otherwise
"bookmark": true // true if current user is in user_bookmark, false otherwise
}
Актуальное поведение
TypeError: 'in ' требует строку в качестве левого операнда, а не SimpleLazyObject
Редактирование 1: Ответ из здесь, похоже, помогает устранить ошибку. К сожалению, аннотированное поле, похоже, не возвращается сериализатором
отредактированный вид:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.prefetch_related('user').all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated]
@action(detail=False, methods=['get'], url_path='current-profile', url_name='current-profile')
def current_user_posts(self, request):
queryset = self.get_queryset().filter(user=request.user).annotate(
bookmark=Exists(Post.user_bookmark.through.objects.filter(
post_id=OuterRef('pk'), user_id=request.user.id))
)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Что вы можете сделать, так это добавить ваши пользовательские поля в сериализатор с помощью SerializerMethodField и передать request.user через get_serializer_context в ваше представление. Например:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.prefetch_related('user').all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated]
@action(detail=False, methods=['get'], url_path='current-profile', url_name='current-profile')
def current_user_posts(self, request):
queryset = self.get_queryset().filter(user=request.user)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
def get_serializer_context(self):
context = super(PostViewSet, self).get_serializer_context()
context.update({"request": self.request})
return context
Это позволяет вам отправить request через context, которые могут быть использованы сериализатором. Теперь в вашем сериализаторе вы можете добавить эти два новых поля:
class PostSerializer(TaggitSerializer, serializers.ModelSerializer):
tags = TagListSerializerField()
bookmark = serializers.SerializerMethodField()
like = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('title','user', 'image','description','date_added', 'tags', 'total_likes', 'bookmark', 'like')
def get_like(self, obj):
return self.context['request'].user in obj.user_like.all()
def get_bookmark(self, obj):
return self.context['request'].user in obj.user_bookmark.all()