70

I am beginner to Django and currently, I can construct model like this.

enter image description here

models.py

class Car(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    photo = models.ImageField(upload_to='cars')

serializers.py

class CarSerializer(serializers.ModelSerializer):
    class Meta:
        model = Car
        fields = ('id','name','price', 'photo') 

views.py

class CarView(APIView):
    permission_classes = ()
    def get(self, request):
        car = Car.objects.all()
        serializer = CarSerializer(car)
        return Response(serializer.data)

For photo, it doesn't show full URL. How can I show full URL?

Rajat Jain
  • 1,339
  • 2
  • 16
  • 29
Khant Thu Linn
  • 5,905
  • 7
  • 52
  • 120
  • Unless your MEDIA_ROOT is within your static folder, there is no direct URL to the file. – jsfan Feb 20 '16 at 11:51
  • try SerializerMethodField ; http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield –  Feb 20 '16 at 12:02

6 Answers6

135

Django is not providing an absolute URL to the image stored in a models.ImageField (at least if you don't include the domain name in the MEDIA_URL; including the domain is not recommended, except of you are hosting your media files on a different server (e.g. aws)).

However, you can modify your serializer to return the absolute URL of your photo by using a custom serializers.SerializerMethodField. In this case, your serializer needs to be changed as follows:

class CarSerializer(serializers.ModelSerializer):
    photo_url = serializers.SerializerMethodField()

    class Meta:
        model = Car
        fields = ('id','name','price', 'photo_url') 

    def get_photo_url(self, car):
        request = self.context.get('request')
        photo_url = car.photo.url
        return request.build_absolute_uri(photo_url)

Also make sure that you have set Django's MEDIA_ROOTand MEDIA_URL parameters and that you can access a photo via your browser http://localhost:8000/path/to/your/image.jpg.

As piling pointed out, you need to add the request while initialising the serializer in your views.py:

def my_view(request):
    …
    car_serializer = CarSerializer(car, context={"request": request})
    car_serializer.data
blacklwhite
  • 1,888
  • 1
  • 12
  • 17
  • 1
    Do I need to modify in views.py as well? I got error after I change your one in Serializers.py. __init__() takes exactly 2 arguments (1 given) – Khant Thu Linn Feb 20 '16 at 11:59
  • Not sure if you can add a QuerySet into the Serializer class. Can you try it with one element only ```serializer = CarSerializer(car[0])``` ? – blacklwhite Feb 20 '16 at 12:12
  • 1
    I still have that error though. I uploaded my project here. In case, if it is necessary to check setting also. http://www.mediafire.com/download/wj3ey2pfx3ygyf5/todoapi.zip – Khant Thu Linn Feb 20 '16 at 12:16
  • looks like you placed the ```photo_url``` inside the ```Meta``` class. Place it one scope above and it should do the trick. – blacklwhite Feb 20 '16 at 12:25
  • 1
    If not try this ; serialized_car = CarSerializer(Car, context={"request": request}) –  Feb 20 '16 at 12:28
  • As it is shown in my answer. Additionally @piling provides the right direction for modifications of the views.py. – blacklwhite Feb 20 '16 at 12:36
  • 1
    I got it now. Thanks both of you. But for photo_url, I need to change like this. photo_url = serializers.SerializerMethodField('get_photo_url') – Khant Thu Linn Feb 20 '16 at 12:53
  • I was missing the request object. Thanks! – Irfan wani Jul 17 '21 at 07:34
61

For future visitors, there is no need to add another field to the serializer if the view method already returns a serialized object. The only thing required is to add the context since it is needed to generate hyperlinks, as stated in the drf documentation

@list_route()
def my_view(self, request):
    qs = Object.objects.all()
    return Response(MySerializer(qs, many=True, context={'request': request}).data)
Hakim
  • 3,225
  • 5
  • 37
  • 75
  • 4
    When using django rest framework views, the context is passed along automatically. If you then use nested serializers without passing the context through, you end up in a confusing situation. So this is very good to know! – David Schumann Jul 15 '19 at 13:50
  • I'm using django rest framework, and this solution was required to output the fully qualfiied URL path. – MikeyE Jan 01 '20 at 05:19
23

Serializer class

class CarSerializer(serializers.ModelSerializer):

  photo_url = serializers.ImageField(max_length=None, use_url=True, allow_null=True, required=False)
  class Meta:
      model = Car
      fields = ('id','name','price', 'photo_url')

View

class CarView(APIView):

    def get(self, request, *args, **kwargs):

        queryset = Car.objects.all()

        serializer = CarSerializer(queryset, many=True, context={"request":request})

        return Response(serializer.data, status=status.HTTP_200_OK)
Njeru Cyrus
  • 1,753
  • 21
  • 22
10

It's better to use this code, due to the above code doesn't check the image is null able or not.

class CarSerializer(serializers.ModelSerializer):
      photo_url = serializers.SerializerMethodField()

      class Meta:
            model = Car
            fields = ('id','name','price', 'photo_url') 

      def get_photo_url(self, car):
            request = self.context.get('request')
            if photo and hasattr(photo, 'url'):
               photo_url = car.photo.url
               return request.build_absolute_uri(photo_url)
            else:
               return None
4

serializers.py

 class BannerSerializer(serializers.ModelSerializer):
        image = serializers.SerializerMethodField()
        def get_image(self, obj):
            return self.context['request'].build_absolute_uri( obj.image.url)

views.py

            banner = Banner.objects.all()
            banner_data = BannerSerializer(banner,many=True, context={'request': request})
            data = banner_data.data
            return Response({"data":data})
abhishek kumar
  • 448
  • 4
  • 10
1

I read the implement of the Serializer, and find the simplest way is to extends ImageField:

from django.db import models

class ImageField(models.ImageField):
    def value_to_string(self, obj): # obj is Model instance, in this case, obj is 'Class'
        return obj.fig.url # not return self.url

class Class(models.Model):
    name = models.CharField(max_length=50)
    intro = models.CharField(max_length=200)
    # fig = models.ImageField(upload_to="classes")
    fig = ImageField(upload_to="classes") 

    def __str__(self):
        return repr(self,"name")
Sailist
  • 134
  • 8