Django REST Framework y FileField absolute url

He definido una aplicación Django simple que incluye el siguiente modelo:

class Project(models.Model): name = models.CharField(max_length=200) thumbnail = models.FileField(upload_to='media', null=True) 

(Técnicamente sí, eso podría haber sido un ImageField).

En una plantilla, es bastante fácil incluir el valor MEDIA_URL (debidamente codificado en settings.py) como un prefijo a la URL de la miniatura. Lo siguiente funciona bien:

 
thumbnail

Usando DRF, he definido un descendiente HyperlinkedModelSerializer llamado ProjectSerializer:

 class ProjectSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Project fields = ( 'id' ,'url', 'name', 'thumbnail') 

Y he definido un descendiente ModelViewSet muy directo:

 class ProjectViewSet(viewsets.ModelViewSet): queryset = Project.objects.all() serializer_class = ProjectSerializer 

Una muestra del JSON resultante se ve así:

 { "id": 1, "url": "http://localhost:8000/api/v1/projects/1/", "name": "Institutional", "thumbnail": "media/institutional_thumb_1.jpg" } 

Todavía no he podido averiguar cómo proporcionar un campo de miniaturas que incluya la url completa a la imagen en la representación JSON de mi proyecto.

Pensaría que tendría que crear un campo personalizado en el ProjectSerializer, pero no he tenido éxito.

Pruebe SerializerMethodField

Ejemplo (no probado):

 class MySerializer(serializers.ModelSerializer): thumbnail_url = serializers.SerializerMethodField('get_thumbnail_url') def get_thumbnail_url(self, obj): return self.context['request'].build_absolute_uri(obj.thumbnail_url) 

La solicitud debe estar disponible para el serializador, de modo que pueda crear la URL absoluta completa para usted. Una forma es pasarlo explícitamente cuando se crea el serializador, similar a esto:

 serializer = MySerializer(account, context={'request': request}) 

Gracias, Shavenwarthog. Su referencia de ejemplo y documentación ayudó enormemente. Mi implementación es ligeramente diferente, pero muy cercana a lo que publicaste:

 from SomeProject import settings class ProjectSerializer(serializers.HyperlinkedModelSerializer): thumbnail_url = serializers.SerializerMethodField('get_thumbnail_url') def get_thumbnail_url(self, obj): return '%s%s' % (settings.MEDIA_URL, obj.thumbnail) class Meta: model = Project fields = ('id', 'url', 'name', 'thumbnail_url') 

Para obtener la url de un archivo que usa FileField, simplemente puede llamar al atributo url del FieldFile (esta es la instancia del archivo, no el campo), usa la clase Storage para determinar la url para este archivo. Es muy sencillo si está utilizando un almacenamiento externo como Amazon S3 o si su almacenamiento cambia.

El get_thumbnail_url sería así.

 def get_thumbnail_url(self, obj): return obj.thumbnail.url 

También puedes usarlo en la plantilla de esta manera:

 {{ current_project.thumbnail.url }} 

Me resultó molesto escribir el mismo código para un campo de método serializado. Si ha configurado correctamente el MEDIA_ROOT en su URL de cubeta de S3, puede agregar un campo al serializador como:

 class ProjectSerializer(serializers.ModelSerializer): logo_url = serializers.URLField(read_only=True, source='logo.url') class Meta: model = Project 

logo es un ImageField en el modelo. no debe ser anulable para evitar errores como ValueError: The 'img' attribute has no file associated with it.

Solo uso .build_absolute_uri en un campo de método de serializador para devolver direcciones URL absolutas que usan otras vistas en mi API. por ejemplo, en mi proyecto hay una URL /webviews/projects/ que muestra un título y un botón que recostack información del usuario (es decir, no es exactamente lo que haría con los sufijos, ya que no es una simple representación de recurso pero incluye algo de lógica en su lugar). el punto final /projects// contiene un campo “webview_url” que se coloca allí, que se genera con SerializerMethodField. no es un medio

Revisa tu configuración.py

ajustes de medios

Tuve el mismo error y encontré que:

MEDIA_URL = ‘/ media /’ hizo el truco.

Antes solo tenía

MEDIA_URL = ‘media /’

Simplemente pase el contexto y pase el objeto de solicitud. si estas usando @api_view

 serializer = CustomerSerializer(customer, context={"request": request}) 

Para el método get_serializer_context del usuario de ViewSet

 class ProjectViewSet(viewsets.ModelViewSet): queryset = Project.objects.all() serializer_class = ProjectSerializer def get_serializer_context(self): return {'request': self.request} 

No hay necesidad de anulaciones o personalizaciones. DRF lo maneja automáticamente. Eche un vistazo al método de FileField de FileField :

 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 

Tenga en cuenta que no funcionará si el contexto del serializador no está configurado correctamente. Si está utilizando ViewSet s, no se preocupe, todo se hace de forma silenciosa, pero si está creando una instancia del serializador manualmente, debe pasar la solicitud en el contexto.

 context = {'request': request} serializer = ExampleSerializer(instance, context=context) return Response(serializer.data) 

https://www.django-rest-framework.org/community/3.0-announcement/#file-fields-as-urls