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
|
||||
|
||||
|
||||
class ByostorageConfig(AppConfig):
|
||||
class BYOStorageConfig(AppConfig):
|
||||
name = 'byostorage'
|
||||
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.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
|
||||
@ -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
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