FileField в Tastypie - PullRequest
       0

FileField в Tastypie

1 голос
/ 26 февраля 2012

У меня есть модель, для которой я делаю api в tastypie.У меня есть поле, в котором хранится путь к файлу, который я веду вручную (я не использую FileField, поскольку пользователи не загружают файлы).Вот суть модели:

class FooModel(models.Model):
    path = models.CharField(max_length=255, null=True)
    ...
    def getAbsPath(self):
        """
        returns the absolute path to a file stored at location self.path
        """
        ...

Вот мой конфиг вкусного пирога:

class FooModelResource(ModelResource):
    file = fields.FileField()

    class Meta:
        queryset = FooModel.objects.all()

    def dehydrate_file(self, bundle):
        from django.core.files import File
        path = bundle.obj.getAbsPath()        
        return File(open(path, 'rb'))

В API в поле файла это возвращает полный путь к файлу.Я хочу, чтобы вкусный пирог мог обслуживать реальный файл или хотя бы URL-адрес файла.Как я могу это сделать?Любые фрагменты кода приветствуются.

Спасибо

Ответы [ 2 ]

4 голосов
/ 26 февраля 2012

Определите схему URL, как ваши файлы будут отображаться через API в первую очередь. Вам на самом деле не нужен файл или файл dehydrate_file (если вы не хотите изменить представление файла для самой модели в Tastypie). Вместо этого просто добавьте дополнительное действие в ModelResource. Пример:

class FooModelResource(ModelResource):
    file = fields.FileField()

    class Meta:
        queryset = FooModel.objects.all()

    def override_urls(self):
        return [
            url(r"^(?P<resource_name>%s)/(?P<pk>\w[\w/-]*)/download%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('download_detail'), name="api_download_detail"),
            ]

    def download_detail(self, request, **kwargs):
        """
        Send a file through TastyPie without loading the whole file into
        memory at once. The FileWrapper will turn the file object into an
        iterator for chunks of 8KB.

        No need to build a bundle here only to return a file, lets look into the DB directly
        """
        filename = self._meta.queryset.get(pk=kwargs[pk]).file
        wrapper = FileWrapper(file(filename))
        response = HttpResponse(wrapper, content_type='text/plain') #or whatever type you want there
        response['Content-Length'] = os.path.getsize(filename)
        return response

GET ... / api / foomodel / 3 /

Возвращает: { ... 'file': 'localpath / filename.ext', ... }

GET ... / api / foomodel / 3 / загрузить /

Возвращает: ... содержимое файла ...

В качестве альтернативы вы можете создать файл подресурса не-ORM в FooModel. Вам нужно будет определить resource_uri (как уникально идентифицировать каждый экземпляр ресурса) и переопределить dispatch_detail, чтобы сделать именно то, что делает download_detail выше.

0 голосов
/ 26 февраля 2012

Единственное преобразование tastypie, выполняемое в FileField, - это поиск атрибута 'url' в возвращаемом вами объекте и возвращение его, если он существует, в противном случае он вернет строковый объект, который, как вы заметили, являетсяfilename.

Если вы хотите вернуть содержимое файла в виде поля, вам нужно будет обработать кодировку файла.У вас есть несколько вариантов:

  • Simplest: используйте CharField и используйте модуль base64 для преобразования байтов, прочитанных из файла, в строку
  • Более общий, но функционально эквивалентный: написать собственный Serializer tastypie, который знает, как превратить объекты File в строковые представления их содержимого
  • Переопределить функцию get_detail вашего ресурса, чтобы обслуживать только файл, используя любой подходящий тип содержимого, чтобы избежатьЗатраты на сериализацию JSON / XML.
...