Add basic support for "download" and "rawbytes" API views
This commit is contained in:
parent
fd1342c605
commit
4d8e29c892
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2020 Lance Edgar
|
# Copyright © 2010-2021 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -26,6 +26,7 @@ Tailbone Web API - Master View (v2)
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
from pyramid.response import FileResponse
|
||||||
from cornice import resource, Service
|
from cornice import resource, Service
|
||||||
|
|
||||||
from tailbone.api import APIMasterView
|
from tailbone.api import APIMasterView
|
||||||
|
@ -41,6 +42,8 @@ class APIMasterView2(APIMasterView):
|
||||||
editable = True
|
editable = True
|
||||||
deletable = True
|
deletable = True
|
||||||
supports_autocomplete = False
|
supports_autocomplete = False
|
||||||
|
supports_download = False
|
||||||
|
supports_rawbytes = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def establish_method(cls, method_name):
|
def establish_method(cls, method_name):
|
||||||
|
@ -85,6 +88,48 @@ class APIMasterView2(APIMasterView):
|
||||||
self.Session.delete(obj)
|
self.Session.delete(obj)
|
||||||
self.Session.flush()
|
self.Session.flush()
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# download
|
||||||
|
##############################
|
||||||
|
|
||||||
|
def download(self):
|
||||||
|
"""
|
||||||
|
GET view allowing for download of a single file, which is attached to a
|
||||||
|
given record.
|
||||||
|
"""
|
||||||
|
obj = self.get_object()
|
||||||
|
|
||||||
|
filename = self.request.GET.get('filename', None)
|
||||||
|
if not filename:
|
||||||
|
raise self.notfound()
|
||||||
|
path = self.download_path(obj, filename)
|
||||||
|
|
||||||
|
response = self.file_response(path)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def download_path(self, obj, filename):
|
||||||
|
"""
|
||||||
|
Should return absolute path on disk, for the given object and filename.
|
||||||
|
Result will be used to return a file response to client.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def rawbytes(self):
|
||||||
|
"""
|
||||||
|
GET view allowing for direct access to the raw bytes of a file, which
|
||||||
|
is attached to a given record. Basically the same as 'download' except
|
||||||
|
this does not come as an attachment.
|
||||||
|
"""
|
||||||
|
obj = self.get_object()
|
||||||
|
|
||||||
|
filename = self.request.GET.get('filename', None)
|
||||||
|
if not filename:
|
||||||
|
raise self.notfound()
|
||||||
|
path = self.download_path(obj, filename)
|
||||||
|
|
||||||
|
response = self.file_response(path, attachment=False)
|
||||||
|
return response
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def defaults(cls, config):
|
def defaults(cls, config):
|
||||||
cls._defaults(config)
|
cls._defaults(config)
|
||||||
|
@ -137,5 +182,24 @@ class APIMasterView2(APIMasterView):
|
||||||
if cls.supports_autocomplete:
|
if cls.supports_autocomplete:
|
||||||
autocomplete = Service(name='{}.autocomplete'.format(route_prefix),
|
autocomplete = Service(name='{}.autocomplete'.format(route_prefix),
|
||||||
path='{}/autocomplete'.format(collection_url_prefix))
|
path='{}/autocomplete'.format(collection_url_prefix))
|
||||||
autocomplete.add_view('GET', 'autocomplete', klass=cls)
|
autocomplete.add_view('GET', 'autocomplete', klass=cls,
|
||||||
|
permission='{}.list'.format(permission_prefix))
|
||||||
config.add_cornice_service(autocomplete)
|
config.add_cornice_service(autocomplete)
|
||||||
|
|
||||||
|
# download
|
||||||
|
if cls.supports_download:
|
||||||
|
download = Service(name='{}.download'.format(route_prefix),
|
||||||
|
# TODO: probably should allow for other (composite?) key fields
|
||||||
|
path='{}/{{uuid}}/download'.format(object_url_prefix))
|
||||||
|
download.add_view('GET', 'download', klass=cls,
|
||||||
|
permission='{}.download'.format(permission_prefix))
|
||||||
|
config.add_cornice_service(download)
|
||||||
|
|
||||||
|
# rawbytes
|
||||||
|
if cls.supports_rawbytes:
|
||||||
|
rawbytes = Service(name='{}.rawbytes'.format(route_prefix),
|
||||||
|
# TODO: probably should allow for other (composite?) key fields
|
||||||
|
path='{}/{{uuid}}/rawbytes'.format(object_url_prefix))
|
||||||
|
rawbytes.add_view('GET', 'rawbytes', klass=cls,
|
||||||
|
permission='{}.download'.format(permission_prefix))
|
||||||
|
config.add_cornice_service(rawbytes)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2020 Lance Edgar
|
# Copyright © 2010-2021 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -166,7 +166,7 @@ class View(object):
|
||||||
return render_to_response('json', data,
|
return render_to_response('json', data,
|
||||||
request=self.request)
|
request=self.request)
|
||||||
|
|
||||||
def file_response(self, path, filename=None):
|
def file_response(self, path, filename=None, attachment=True):
|
||||||
"""
|
"""
|
||||||
Returns a generic FileResponse from the given path
|
Returns a generic FileResponse from the given path
|
||||||
"""
|
"""
|
||||||
|
@ -174,6 +174,7 @@ class View(object):
|
||||||
return self.notfound()
|
return self.notfound()
|
||||||
response = FileResponse(path, request=self.request)
|
response = FileResponse(path, request=self.request)
|
||||||
response.content_length = os.path.getsize(path)
|
response.content_length = os.path.getsize(path)
|
||||||
|
if attachment:
|
||||||
if not filename:
|
if not filename:
|
||||||
filename = os.path.basename(path)
|
filename = os.path.basename(path)
|
||||||
if six.PY2:
|
if six.PY2:
|
||||||
|
|
Loading…
Reference in a new issue