0
votes

I need viewset to return always absolute url for avatar / avatar_thumbnail image fields in response, but I'm getting relative paths. I have two such cases: 1st) is in image field in nested serializer, 2nd) is in viewset where I want to use two serializers in retrieve method.

models.py

class CustomUser(AbstractUser):
  avatar = ProcessedImageField(upload_to='users/',format='JPEG',options={'quality': 60})
  avatar_thumbnail = ImageSpecField(source='avatar',processors=[ResizeToFill(50, 50)],format='JPEG')

settings.py

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

urls.py

if settings.DEBUG:
    from django.conf.urls.static import static
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)here

forum/api/serializers.py (nested serializer)

class UserSerializer(serializers.ModelSerializer):
    avatar_thumbnail = serializers.ImageField(read_only=True)

    class Meta:
        model = CustomUser
        fields = ['id', 'username', 'avatar_thumbnail']

class ThreadSerializer(serializers.ModelSerializer):

    class Meta:
        model = Thread
        fields = ['id', 'title', 'subject', 'user']

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['user'] = UserSerializer(instance.user).data
        return representation

forum/api/views.py

class ThreadViewSet(viewsets.ModelViewSet):
    serializer_class = ThreadSerializer
    queryset = Thread.objects.all()

For UserViewSet I have also similar problem with urls for "avatar". I need absolute urls. I get relative url, but only when I overwrite retrieve method in viewset. (I overwrite it to use different serializer "UserPrivateSerializer" for user that is owner of the profile) .For list I always get absolute url.

users/api/serializers.py

class UserPublicSerializer(serializers.ModelSerializer):

class Meta:
    model = CustomUser
    fields = ['id', 'username', 'avatar']

class UserPrivateSerializer(serializers.ModelSerializer):    
    email = serializers.EmailField(required=True)

    class Meta:
        model = CustomUser
        fields = ['id', 'username', 'email', 'avatar']

users/api/views.py (Here the problem is with additional serializer, that I want to use in retrieve method)

class UserViewSet(viewsets.ModelViewSet):
    queryset = CustomUser.objects.exclude(username='user_deleted')
    serializer_class = UserPublicSerializer
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

def retrieve(self, request, *args, **kwargs):
    """Custom retrieve method use UserPrivateSerializer if the user object is requested by it's owner.
    """
    super().retrieve(request, *args, **kwargs)
    instance = self.get_object()
    if request.user == instance:
        serializer = UserPrivateSerializer(instance)
    else:
        serializer = UserPublicSerializer(instance)
    return Response(serializer.data, status=status.HTTP_200_OK)
1

1 Answers

0
votes

I have found out that the problem is solved when I add context to serializers.

forum/api/serializers.py

class ThreadSerializer(serializers.ModelSerializer):

    class Meta:
        model = Thread
        fields = ['id', 'title', 'subject', 'user']

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['user'] = UserSerializer(instance.user, context=self.context).data
        return representation

users/api/views.py

class UserViewSet(viewsets.ModelViewSet):
    queryset = CustomUser.objects.exclude(username='user_deleted')
    serializer_class = UserPublicSerializer
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

    def retrieve(self, request, *args, **kwargs):
        super().retrieve(request, *args, **kwargs)
        instance = self.get_object()
        if request.user == instance:
            serializer = UserPrivateSerializer(instance, context={'request': request})
        else:
            serializer = UserPublicSerializer(instance, context={'request': request})
        return Response(serializer.data, status=status.HTTP_200_OK)

I have found my answer here: Django REST Framework and FileField absolute url "The request must available to the serializer, so it can build the full absolute URL for you. One way is to explicitly pass it in when the serializer is created"