2016-03-15 3 views
1

Я пишу API, который позволяет клиенту размещать ссылку на файл, а затем я хочу загрузить файл и сохранить его как FileField на одной из моих моделей. Вот код, который я до сих пор:Django Rest Framework - Загрузка файлов на сервер

Serializer:

from rest_framework import serializers 
from django.core.files import File 
from rest_framework.serializers import ValidationError 

class MySerializer(serializers.ModelSerializer): 

    class Meta: 
     model = MyModel 
     fields = ('url') 

    def to_internal_value(self, data): 
     validated = dict() 

     url = data.get('url') 

     # do url validation here 

     # download the file to local disk 

     validated['file'] = File(open('path/to/downloaded/file')) 
     validated['url'] = url 

     return validated 

Модель:

from django.db import models 


class MyModel(models.Model): 
    url = models.CharField(max_length=255) 
    file = models.FileField(upload_to='files/') 

Для зрения, я использую общий ListCreateAPIView.

Есть две основные проблемы: во-первых, с моей текущей реализацией я получаю две копии файла, потому что сначала загружаю его в какое-то место на диске, а потом, когда FileField хранится в базе данных, он снова копирует файл в папку files/. Есть ли способ избежать этого? Во-вторых, как я могу загрузить файл асинхронно, но все же могу добавить атрибут FileField на модель после завершения загрузки?

ответ

4

Лучший способ сделать то же самое будет создать свой собственный serializer field

from django.core.validators import URLValidator 
from django.core.files.base import ContentFile 

from rest_framework import serializers 

from urllib.request import urlretrieve 

class FileUrlField(serializers.FileField): 
    def to_internal_value(self, data): 
     try: 
      URLValidator()(data) 
     except ValidationError as e: 
      raise ValidationError('Invalid Url') 

     # download the contents from the URL 
     file, http_message = urlretrieve(data) 
     file = File(open(file, 'rb')) 
     return super(FileUrlField, self).to_internal_value(ContentFile(file.read(), name=file.name)) 

, а затем использовать его в serializer

Однако я не тестировал, но должно работать.

Смежные вопросы