Initial commit

This commit is contained in:
Tris 2021-03-12 11:08:40 +11:00
commit 3f6046fed6
11 changed files with 173 additions and 0 deletions

2
CHANGELOG.rst Normal file
View File

@ -0,0 +1,2 @@
django-byostorage CHANGELOG
===========================

22
README.rst Normal file
View File

@ -0,0 +1,22 @@
Installation
============
Usage
=====
.. code-block:: python
def generate_filename(instance, filename):
return f"{instance.parent.storage}:some_folder/{filename}"
class SomeParentModel(models.Model):
storage = models.ForeignKey('byostorage.UserStorage',
on_delete=models.CASCADE)
class MyModel(models.Model):
parent = models.ForeignKey('SomeParentModel',
on_delete=models.CASCADE)
photo = models.FileField(
storage=BYOStorage(),
upload_to=generate_filename)

1
byostorage/__init__.py Normal file
View File

@ -0,0 +1 @@
__VERSION__ = 0.1

7
byostorage/admin.py Normal file
View File

@ -0,0 +1,7 @@
from django.contrib import admin
from . import models
class UserStorageAdmin(admin.ModelAdmin):
list_display = ['name', 'storage', 'owner']
admin.site.register(models.UserStorage, UserStorageAdmin)

6
byostorage/apps.py Normal file
View File

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

View File

@ -0,0 +1,27 @@
# Generated by Django 3.1.1 on 2021-03-11 23:11
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserStorage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.SlugField(help_text='Storage tag', max_length=20)),
('storage', models.CharField(help_text='Storage class for this instance', max_length=255)),
('settings_data', models.TextField(help_text='JSON dict with key/value settings')),
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

18
byostorage/models.py Normal file
View File

@ -0,0 +1,18 @@
from django.db import models
from django.core.files.storage import get_storage_class
import json
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")
settings_data = models.TextField(help_text="JSON dict with key/value settings")
def instance(self):
return get_storage_class(self.storage)(**self.settings)
@property
def settings(self):
return json.loads(self.settings_data)

52
byostorage/storage.py Normal file
View File

@ -0,0 +1,52 @@
from django.core.files.storage import Storage
class MultiStorage(Storage):
''' Django storage class that proxies multiple storage classes.
Uses a name prefix to determine store e.g. 'images:foo/bar.jpg'
'''
def __init__(self, config=None):
self.config = config or getattr(settings, 'BRING_YOUR_OWN_STORAGE', {})
self._cache = {}
def get_storage(self, name):
if name not in self._cache:
config = self.config[name]
self._cache[name] = get_storage_class(config['storage'])(**config['settings'])
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)
def _proxy(self, 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}"
def _save(self, name, content, max_length=None):
return self._proxy('save', name, content, max_length)
class BYOStorage(MultiStorage):
''' Database driven Bring-Your-Own-Storage
Multiple storages can
'''
def get_storage(self, name):
if name not in self._cache:
# use storage from config by default
if name in self.config:
return super(BYOStorage, self).get_storage(name)
from .models import UserStorage
obj = UserStorage.objects.get(name=name)
self._cache[name] = obj.instance()
return self._cache[name]

35
setup.cfg Normal file
View File

@ -0,0 +1,35 @@
[metadata]
name = django-byostorage
version = attr: byostorage.__version__
description = Support for Bring-Your-Own-Storage
long_description = file: README.rst, CHANGELOG.rst
license = BSD-3-Clause
author = Tris Forster
author_email = tris.forster@gmail.com
url = https://github.com/tf198/django-byostorage
classifiers =
Development Status :: 1 - Development
Environment :: Web Environment
Framework :: Django
Framework :: Django :: 2.2
Framework :: Django :: 3.0
Framework :: Django :: 3.1
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
[options]
python_requires = >=3.5
install_requires =
Django >= 2.2
packages =
byostorage

3
setup.py Normal file
View File

@ -0,0 +1,3 @@
from setuptools import setup
setup()