Django REST prints the method for the complete URL for the file properties

  • 2020-06-15 09:46:02
  • OfStack

preface

The API part of my App project is built by using Django REST Framework, which can be very convenient to build API like building blocks 1, with both convenience and flexibility.

django is one magic framework, and restframework follows another magic framework, but restframework's documentation is so poor that many times you have to look at the source code to write scientific code, which gets in the way of many beginners.

I've also picked up a few tricks along the way, including one on how to output the full URL field for file properties.

Implementation method

In a typical case, when requesting /profile/ this API, something like this is returned:


{
 "id": 1,
 "nickname": " The administrator ",
 "mobilephone": "1234567890",
 "avatar": "/media/profiles/2017/12/17/avatar.png"
}

In the definition of Django REST, I used a custom extension of es28EN_framework.views.APIView's ProfileView type to implement its get method to return an Profile object to the authenticated user:


class ProfileView(APIView):
 def get(self, request):
  user = request.user
  if user.is_authenticated:
   profile = Profile.objects.get(user=user)
   return Response(ProfileSerializer(profile).data)
  else:
   raise exceptions.AuthenticationFailed('Not authenticated user!')

The logic here is simple: determine if the user requesting the current API is an authenticated user, if so, get its Profile, and serialize the profile instance to an JSON object via ProfileSerializer. If the user is not authenticated, information about the 401 validation failure is returned.

It is ok to give the above output to the Web front-end, but if it is used by App, then the file attribute of avatar is not suitable for URL, so we need to modify the 1 API to output absolute URL.

So how do we do that? Just modify the get method above slightly:


-class ProfileView(APIView):
+class ProfileView(generics.GenericAPIView):
  parser_classes = (MultiPartParser, FormParser)
+ serializer_class = ProfileSerializer
  def get(self, request):
   user = request.user
   if user.is_authenticated:
    profile = Profile.objects.get(user=user)
-   return Response(ProfileSerializer(profile).data)
+   serializer = self.get_serializer(profile)
+   return Response(serializer.data)
   else:
    raise exceptions.AuthenticationFailed('Not authenticated user!')

Instead of inheriting from APIView, it now inherits from ES58en.GenericAPIView, which is a more general class, so you can see the difference between manually building ProfileSerializer and using self.get_serializer.

Also need to see Django REST source, GenericAPIView this class get_serializer doing.


def get_serializer(self, *args, **kwargs):
    """
    Return the serializer instance that should be used for validating and
    deserializing input, and for serializing output.
    """
    serializer_class = self.get_serializer_class()
    kwargs['context'] = self.get_serializer_context()
    return serializer_class(*args, **kwargs)

As you can see, when creating serializer, this method will pass in context, and get_serializer_context is also a fixed method, which will include request, view and format.

So how is request, view, and format used in serializer to expand the full path of a file object?

serializer 1 series serialization process is omitted in the middle, when it encounters FileField, it will determine whether there is reuqest in context, if there is, it will call request.build_absolute_uri (url) method, the absolute address build out, rather than the default relative address in the database.


def to_representation(self, value):
  if not value:
   return None
  use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL)
  if use_url:
   if not getattr(value, 'url', None):
    # If the file has not been saved it may not have a URL.
    return None
   url = value.url
   request = self.context.get('request', None)
   if request is not None:
    return request.build_absolute_uri(url)
   return url
  return value.name

That's why when you print API objects from GenericAPIView, the file attributes default to absolute rather than relative paths

conclusion


Related articles: