Django REST Framework et FileField absolute url

j'ai défini une application Django simple qui inclut le modèle suivant:

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

(techniquement, oui, ça aurait pu être un champ D'image.)

dans un modèle, il est assez facile d'inclure la valeur MEDIA_URL (dûment codée dans settings.py) comme préfixe de L'URL de la vignette. Le suivant fonctionne très bien:

<div id="thumbnail"><img src="{{ MEDIA_URL }}{{ current_project.thumbnail }}" alt="thumbnail" width="400" height="300" border="0" /></div>

en utilisant DRF, j'ai défini une descendante hyperliée de Modelserializer appelée ProjectSerializer:

class ProjectSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Project
        fields = ( 'id' ,'url', 'name', 'thumbnail')

Et j'ai défini un très simple modelviewset descendant:

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

un échantillon du JSON résultant ressemble à ceci:

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

Je n'ai pas encore été capable de trouver comment fournir un champ miniature qui inclut l'url complète de l'image dans la représentation JSON de mon projet.

je pense que j'aurais besoin de créer un champ personnalisé dans le ProjectSerializer, mais n'ont pas été réussies.

18
demandé sur Mark Semsel 2014-05-28 21:54:29

7 réponses

SerializerMethodField

Exemple (non testé):

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 requête doit être disponible pour le serialiseur, de sorte qu'il peut construire L'URL absolue complète pour vous. Une façon est de transmettre explicitement dans lorsque le sérialiseur est créé, comme ceci:

serializer = MySerializer(account, context={'request': request})
38
répondu johntellsall 2017-01-19 13:51:32

Merci, shavenwarthog. Votre exemple et votre référence documentaire ont énormément aidé. Mon application est un peu différent, mais très proche de ce que vous avez posté:

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') 
7
répondu Mark Semsel 2014-05-28 18:51:21

Pour obtenir l'url d'un fichier qui utilise FileField, vous pouvez simplement appeler l'url de l'attribut de la FieldFile (c'est le fichier exemple pas le terrain), d'utiliser la classe de Stockage pour déterminer l'url de ce fichier. C'est très simple si vous utilisez un stockage externe comme Amazon S3 ou si votre stockage change.

Le get_thumbnail_url serait comme ça.

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

Vous pouvez également l'utiliser dans le modèle de cette façon:

{{ current_project.thumbnail.url }}
6
répondu Johnny Well 2014-08-06 20:01:11

j'ai trouvé ennuyeux d'écrire le même code pour un champ de méthode sérialisé. Si vous avez positionné correctement le MEDIA_ROOT pour votre S3 seau URL, vous pouvez ajouter un champ à la sérialiseur de la forme:

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

    class Meta:
        model = Project

le logo est un champ D'image dans le modèle. il ne doit pas être nul pour éviter des erreurs comme ValueError: The 'img' attribute has no file associated with it.

je n'utilise .build_absolute_uri dans un champ de méthode serializer pour retourner les URL absolues qui utilisent d'autres vues dans mon API. par exemple, dans mon projet il y a une URL /webviews/projects/<pk> que shows, un titre et un bouton qui recueille certaines entrées de l'utilisateur (c'est-à-dire pas exactement ce que vous feriez avec des suffixes, car ce n'est pas une représentation simple de la ressource mais inclut une certaine logique à la place). le point de fin /projects/<pk>/ contient un champ "webview_url" qui est généré avec SerializerMethodField. c'est pas les médias.

3
répondu Eduard Gamonal 2015-02-22 18:45:17

il suffit de passer le contexte et passer l'objet request. si vous utilisez @api_view

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

Pour ViewSet utilisateur get_serializer_context méthode

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

def get_serializer_context(self):
    return {'request': self.request}
1
répondu Tarikul Islam Rasel 2016-12-19 07:56:02

Vérifiez que vous settings.py

paramètres des médias.

j'ai eu la même erreur et a constaté que:

MEDIA_URL = ' / media/' a fait le tour.

avant j'avais seulement:

MEDIA_URL = ' media/'

0
répondu jakobdo 2016-11-07 19:28:43

Pas besoin de remplacements ou des personnalisations. La FONDATION gère automatiquement. Jetez un oeil à to_representation méthode 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

notez que cela ne fonctionnera pas si le contexte du sérialiseur n'est pas défini correctement. Si vous utilisez ViewSets, pas de soucis, tout se fait en silence, mais si vous êtes à l'instanciation de la sérialiseur manuellement, vous devez passer dans la demande dans le cadre.

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

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

0
répondu MJafar Mash 2018-09-16 18:46:30