Proof of concept

This commit is contained in:
Tris 2021-03-12 13:45:07 +11:00
parent 3f6046fed6
commit 6fd42f3456
9 changed files with 142 additions and 15 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__
*.pyc

View File

@ -1 +1,2 @@
__VERSION__ = 0.1
__version__ = 0.1
default_app_config = 'byostorage.apps.BYOStorageConfig'

View File

@ -1,6 +1,6 @@
from django.apps import AppConfig
class ByostorageConfig(AppConfig):
class BYOStorageConfig(AppConfig):
name = 'byostorage'
verbose_name = 'Bring your own storage'

View File

@ -0,0 +1,6 @@
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'byostorage'
]
SECRET_KEY = 'shh!'

View 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),
),
]

View 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),
),
]

View File

@ -1,13 +1,34 @@
from django.conf import settings
from django.db import models
from django.core.files.storage import get_storage_class
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):
""" A user defined storage
"""
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE, null=True, blank=True)
name = models.SlugField(max_length=20, help_text="Storage tag")
storage = models.CharField(max_length=255, help_text="Storage class for this instance")
owner = models.ForeignKey('auth.User',
on_delete=models.CASCADE,
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")
def instance(self):
@ -15,4 +36,14 @@ class UserStorage(models.Model):
@property
def settings(self):
return json.loads(self.settings_data)
if not self.settings_data:
return {}
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

View File

@ -1,4 +1,9 @@
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):
''' Django storage class that proxies multiple storage classes.
@ -17,21 +22,51 @@ class MultiStorage(Storage):
return self._cache[name]
def split(self, name):
return name.split(":", 1)
def _open(self, name, mode='rb'):
storage, p = self.split(name)
return self.get_storage(storage)._open(p, mode)
return name.split("/", 1)
def _proxy(self, method, name, *args, **kwargs):
print('PROXY', method, name, *args, **kwargs)
storage, p = self.split(name)
sname = getattr(self.get_storage(storage), method)(name, *args, **kwargs)
if sname is not None:
return f"{storage}:{sname}"
result = getattr(self.get_storage(storage), method)(p, *args, **kwargs)
print("RESULT", result)
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):
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):
''' Database driven Bring-Your-Own-Storage

11
manage.py Executable file
View 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)