From 0e08ddc327d4bc713e1e8c07fa98861c67d0693d Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Mon, 15 Sep 2014 09:25:10 +0200 Subject: [PATCH] Add tenant based storage handler (fixes #5501) To use it, add this to your settings.py: DEFAULT_FILE_STORAGE = 'entrouvert.djommon.multitenant.storage.TenantFileSystemStorage' --- hobo/multitenant/storage.py | 24 ++++++++++++++++++++++++ hobo/multitenant/tests.py | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 hobo/multitenant/storage.py diff --git a/hobo/multitenant/storage.py b/hobo/multitenant/storage.py new file mode 100644 index 0000000..c127891 --- /dev/null +++ b/hobo/multitenant/storage.py @@ -0,0 +1,24 @@ +import os + +from django.conf import settings +from django.core.exceptions import SuspiciousOperation +from django.utils._os import safe_join + +from django.db import connection + +from django.core.files.storage import FileSystemStorage + +__all__ = ('TenantFileSystemStorage', ) + +class TenantFileSystemStorage(FileSystemStorage): + '''Lookup files first in $TENANT_BASE//media/ then in default location''' + def path(self, name): + if connection.tenant: + location = safe_join(settings.TENANT_BASE, connection.tenant.schema_name, 'media') + else: + location = self.location + try: + path = safe_join(location, name) + except ValueError: + raise SuspiciousOperation("Attempted access to '%s' denied." % name) + return os.path.normpath(path) diff --git a/hobo/multitenant/tests.py b/hobo/multitenant/tests.py index 81f3e76..92272a3 100644 --- a/hobo/multitenant/tests.py +++ b/hobo/multitenant/tests.py @@ -6,6 +6,7 @@ import tempfile import shutil import os import json +import StringIO from django.conf.urls import patterns from django.test import TestCase, Client @@ -29,10 +30,21 @@ def python_key(request, *args, **kwargs): def template(request, *args, **kwargs): return TemplateResponse(request, 'tenant.html') +def upload(request): + from django.core.files.storage import default_storage + default_storage.save('upload', request.FILES['upload']) + return HttpResponse('') + +def download(request): + from django.core.files.storage import default_storage + return HttpResponse(default_storage.open('upload').read()) + urlpatterns = patterns('', ('^json_key/$', json_key), ('^python_key/$', python_key), ('^template/$', template), + ('^upload/$', upload), + ('^download/$', download), ) @override_settings( @@ -44,7 +56,8 @@ urlpatterns = patterns('', ), TEMPLATE_LOADERS = ( 'entrouvert.djommon.multitenant.template_loader.FilesystemLoader', - ) + ), + DEFAULT_FILE_STORAGE = 'entrouvert.djommon.multitenant.storage.TenantFileSystemStorage', ) class SimpleTest(TestCase): TENANTS = ['tenant1', 'tenant2'] @@ -65,6 +78,8 @@ class SimpleTest(TestCase): tenant_html = os.path.join(templates_dir, 'tenant.html') with file(tenant_html, 'w') as f: print >>f, tenant + ' template', + media_dir = os.path.join(tenant_dir, 'media') + os.mkdir(media_dir) def tearDown(self): shutil.rmtree(self.tenant_base, ignore_errors=True) @@ -96,5 +111,18 @@ class SimpleTest(TestCase): domain_url=tenant)) for tenant in self.TENANTS) self.assertEquals(l1, l2) - - + def test_storage(self): + from django.core.files.base import ContentFile + with self.tenant_settings(): + for tenant in self.TENANTS: + c = Client(HTTP_HOST=tenant) + uploaded_file_path = os.path.join(self.tenant_base, tenant, 'media', 'upload') + self.assertFalse(os.path.exists(uploaded_file_path), uploaded_file_path) + response = c.post('/upload/', {'upload': ContentFile(tenant + ' upload', name='upload.txt')}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, '') + self.assertTrue(os.path.exists(uploaded_file_path)) + self.assertEqual(file(uploaded_file_path).read(), tenant + ' upload') + response = c.get('/download/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, tenant + ' upload')