python - relations - Django REST Framework y FileField absolute url
modelserializer django rest framework (7)
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:
<div id="thumbnail"><img src="{{ MEDIA_URL }}{{ current_project.thumbnail }}" alt="thumbnail" width="400" height="300" border="0" /></div>
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.
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'')
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/<pk>
que muestra un título y un botón que recopila 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/<pk>/
contiene un campo "webview_url" que se coloca allí, que se genera con SerializerMethodField. no es un medio
No hay necesidad de anulaciones o personalizaciones. DRF lo maneja automáticamente. Echa un vistazo a to_representation
method of 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 manera 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
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 }}
Prueba 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})
Revisa tu configuración.py
ajustes de medios
Tuve el mismo error y encontré que:
MEDIA_URL = ''/ media /'' hizo el truco.
Antes solo tenia:
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 usuario de ViewSet, método get_serializer_context
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
def get_serializer_context(self):
return {''request'': self.request}