from django.conf import settings from django.db import models from django.core.files.storage import get_storage_class, FileSystemStorage from django.core.exceptions import ValidationError 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 ] def validate_json(value): try: json.loads(value) except json.JSONDecodeError as e: raise ValidationError(e.msg) 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, 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( validators=[validate_json], help_text="JSON dict with key/value settings") def instance(self): return get_storage_class(self.storage)(**self.settings) @property def settings(self): if not self.settings_data: return {} try: return json.loads(self.settings_data) except Exception as e: raise ValueError("Error in settings for storage '{0}' [{1}]".format(self.name, e)) def clean(self): try: self.test_storage() except Exception as e: raise ValidationError(str(e)) def test_storage(self): # just do something that requires connection try: self.instance().listdir('') except FileNotFoundError: # FileSystemStorage doesn't create the base_dir until write pass return True def save(self, *args, **kwargs): # ensure the settings JSON isvalid if self.settings_data: validate_json(self.settings_data) super(UserStorage, self).save(*args, **kwargs) def __str__(self): return self.name