Proof of concept
This commit is contained in:
parent
3f6046fed6
commit
6fd42f3456
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
@ -1 +1,2 @@
|
|||||||
__VERSION__ = 0.1
|
__version__ = 0.1
|
||||||
|
default_app_config = 'byostorage.apps.BYOStorageConfig'
|
||||||
@ -1,6 +1,6 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class ByostorageConfig(AppConfig):
|
class BYOStorageConfig(AppConfig):
|
||||||
name = 'byostorage'
|
name = 'byostorage'
|
||||||
verbose_name = 'Bring your own storage'
|
verbose_name = 'Bring your own storage'
|
||||||
|
|||||||
6
byostorage/local_settings.py
Normal file
6
byostorage/local_settings.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'byostorage'
|
||||||
|
]
|
||||||
|
SECRET_KEY = 'shh!'
|
||||||
18
byostorage/migrations/0002_auto_20210311_1826.py
Normal file
18
byostorage/migrations/0002_auto_20210311_1826.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.1.7 on 2021-03-11 18:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('byostorage', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='userstorage',
|
||||||
|
name='storage',
|
||||||
|
field=models.CharField(choices=[], help_text='Storage class for this instance', max_length=255),
|
||||||
|
),
|
||||||
|
]
|
||||||
23
byostorage/migrations/0003_auto_20210311_1917.py
Normal file
23
byostorage/migrations/0003_auto_20210311_1917.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 3.1.7 on 2021-03-11 19:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('byostorage', '0002_auto_20210311_1826'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='userstorage',
|
||||||
|
name='name',
|
||||||
|
field=models.SlugField(help_text='Storage tag', max_length=20, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='userstorage',
|
||||||
|
name='storage',
|
||||||
|
field=models.CharField(choices=[('django.core.files.storage.FileSystemStorage', 'django.core.files.storage.FileSystemStorage')], help_text='Storage class for this instance', max_length=255),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,13 +1,34 @@
|
|||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.files.storage import get_storage_class
|
from django.core.files.storage import get_storage_class
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
STORAGE_CLASSES = getattr(settings, 'STORAGE_CLASSES', [])
|
||||||
|
|
||||||
|
STORAGE_CLASSES.extend([
|
||||||
|
'django.core.files.storage.FileSystemStorage',
|
||||||
|
])
|
||||||
|
|
||||||
|
try:
|
||||||
|
import storages
|
||||||
|
STORAGE_CLASSES.append('storages.backends.s3boto3.S3Boto3Storage')
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
STORAGE_CLASS_OPTIONS = [ (x, x) for x in STORAGE_CLASSES ]
|
||||||
|
|
||||||
class UserStorage(models.Model):
|
class UserStorage(models.Model):
|
||||||
""" A user defined storage
|
""" A user defined storage
|
||||||
"""
|
"""
|
||||||
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE, null=True, blank=True)
|
owner = models.ForeignKey('auth.User',
|
||||||
name = models.SlugField(max_length=20, help_text="Storage tag")
|
on_delete=models.CASCADE,
|
||||||
storage = models.CharField(max_length=255, help_text="Storage class for this instance")
|
null=True, blank=True)
|
||||||
|
name = models.SlugField(max_length=20,
|
||||||
|
unique=True,
|
||||||
|
help_text="Storage tag")
|
||||||
|
storage = models.CharField(max_length=255,
|
||||||
|
choices=STORAGE_CLASS_OPTIONS,
|
||||||
|
help_text="Storage class for this instance")
|
||||||
settings_data = models.TextField(help_text="JSON dict with key/value settings")
|
settings_data = models.TextField(help_text="JSON dict with key/value settings")
|
||||||
|
|
||||||
def instance(self):
|
def instance(self):
|
||||||
@ -15,4 +36,14 @@ class UserStorage(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def settings(self):
|
def settings(self):
|
||||||
|
if not self.settings_data:
|
||||||
|
return {}
|
||||||
return json.loads(self.settings_data)
|
return json.loads(self.settings_data)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
# check the settings are valid
|
||||||
|
self.settings
|
||||||
|
super(UserStorage, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
@ -1,4 +1,9 @@
|
|||||||
from django.core.files.storage import Storage
|
from django.core.files.storage import Storage
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
'''
|
||||||
|
TODO: Create a signal to remove instances from cache if modified
|
||||||
|
'''
|
||||||
|
|
||||||
class MultiStorage(Storage):
|
class MultiStorage(Storage):
|
||||||
''' Django storage class that proxies multiple storage classes.
|
''' Django storage class that proxies multiple storage classes.
|
||||||
@ -17,21 +22,51 @@ class MultiStorage(Storage):
|
|||||||
return self._cache[name]
|
return self._cache[name]
|
||||||
|
|
||||||
def split(self, name):
|
def split(self, name):
|
||||||
return name.split(":", 1)
|
return name.split("/", 1)
|
||||||
|
|
||||||
def _open(self, name, mode='rb'):
|
|
||||||
storage, p = self.split(name)
|
|
||||||
return self.get_storage(storage)._open(p, mode)
|
|
||||||
|
|
||||||
def _proxy(self, method, name, *args, **kwargs):
|
def _proxy(self, method, name, *args, **kwargs):
|
||||||
|
print('PROXY', method, name, *args, **kwargs)
|
||||||
storage, p = self.split(name)
|
storage, p = self.split(name)
|
||||||
sname = getattr(self.get_storage(storage), method)(name, *args, **kwargs)
|
result = getattr(self.get_storage(storage), method)(p, *args, **kwargs)
|
||||||
if sname is not None:
|
print("RESULT", result)
|
||||||
return f"{storage}:{sname}"
|
return result
|
||||||
|
|
||||||
|
def _proxy_name(self, method, name, *args, **kwargs):
|
||||||
|
print('PROXY_NAME', method, name, *args, **kwargs)
|
||||||
|
storage, p = self.split(name)
|
||||||
|
result = getattr(self.get_storage(storage), method)(p, *args, **kwargs)
|
||||||
|
print("RESULT", result)
|
||||||
|
return f"{storage}/{result}"
|
||||||
|
|
||||||
|
def _open(self, name, mode='rb'):
|
||||||
|
return self._proxy('_open', name, mode)
|
||||||
|
|
||||||
def _save(self, name, content, max_length=None):
|
def _save(self, name, content, max_length=None):
|
||||||
return self._proxy('save', name, content, max_length)
|
return self._proxy_name('_save', name, content, max_length)
|
||||||
|
|
||||||
|
def exists(self, name):
|
||||||
|
return self._proxy('exists', name)
|
||||||
|
|
||||||
|
def delete(self, name):
|
||||||
|
return self._proxy('delete', name)
|
||||||
|
|
||||||
|
def url(self, name):
|
||||||
|
return self._proxy('url', name)
|
||||||
|
|
||||||
|
def get_accessed_time(self, name):
|
||||||
|
return self._proxy('get_accessed_time', name)
|
||||||
|
|
||||||
|
def get_alternative_name(self, file_root, file_ext):
|
||||||
|
return self._proxy_name('get_alternative_name', file_root, file_ext)
|
||||||
|
|
||||||
|
def get_available_name(self, name, max_length=None):
|
||||||
|
return self._proxy_name('get_available_name', name, max_length)
|
||||||
|
|
||||||
|
def get_modified_time(self, name):
|
||||||
|
return self._proxy('get_modified_time', name)
|
||||||
|
|
||||||
|
def get_valid_name(self, name):
|
||||||
|
return self._proxy_name('get_valid_name', name)
|
||||||
|
|
||||||
class BYOStorage(MultiStorage):
|
class BYOStorage(MultiStorage):
|
||||||
''' Database driven Bring-Your-Own-Storage
|
''' Database driven Bring-Your-Own-Storage
|
||||||
|
|||||||
11
manage.py
Executable file
11
manage.py
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLED_APPS = ['byostorage']
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'byostorage.local_settings')
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
Loading…
x
Reference in New Issue
Block a user