diff options
Diffstat (limited to 'examples/s3project')
| -rw-r--r-- | examples/s3project/__init__.py | 0 | ||||
| -rw-r--r-- | examples/s3project/manage.py | 15 | ||||
| -rw-r--r-- | examples/s3project/models.py | 25 | ||||
| -rw-r--r-- | examples/s3project/settings.py | 52 | ||||
| -rw-r--r-- | examples/s3project/tests.py | 200 |
5 files changed, 292 insertions, 0 deletions
diff --git a/examples/s3project/__init__.py b/examples/s3project/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/examples/s3project/__init__.py diff --git a/examples/s3project/manage.py b/examples/s3project/manage.py new file mode 100644 index 0000000..77c9127 --- /dev/null +++ b/examples/s3project/manage.py @@ -0,0 +1,15 @@ +# put patched django and S3 in PYTHONPATH +import sys, os +sys.path = [os.path.join(os.getcwd(), '../../')] + sys.path + +from django.core.management import execute_manager + +try: + import settings # Assumed to be in the same directory. +except ImportError: + import sys + sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) + sys.exit(1) + +if __name__ == "__main__": + execute_manager(settings) diff --git a/examples/s3project/models.py b/examples/s3project/models.py new file mode 100644 index 0000000..0a4815d --- /dev/null +++ b/examples/s3project/models.py @@ -0,0 +1,25 @@ + +import tempfile + +from django.db import models +from django.core.files.base import ContentFile +from django.core.files.storage import default_storage as s3_storage +from django.core.cache import cache + +# Write out a file to be used as default content +s3_storage.save('tests/default.txt', ContentFile('default content')) + +class MyStorage(models.Model): + def custom_upload_to(self, filename): + return 'foo' + + def random_upload_to(self, filename): + # This returns a different result each time, + # to make sure it only gets called once. + import random + return '%s/%s' % (random.randint(100, 999), filename) + + normal = models.FileField(storage=s3_storage, upload_to='tests') + custom = models.FileField(storage=s3_storage, upload_to=custom_upload_to) + random = models.FileField(storage=s3_storage, upload_to=random_upload_to) + default = models.FileField(storage=s3_storage, upload_to='tests', default='tests/default.txt') diff --git a/examples/s3project/settings.py b/examples/s3project/settings.py new file mode 100644 index 0000000..3ac8133 --- /dev/null +++ b/examples/s3project/settings.py @@ -0,0 +1,52 @@ +import os +ROOT_PATH = os.path.dirname(__file__) + +TEMPLATE_DEBUG = DEBUG = True +MANAGERS = ADMINS = () +DATABASE_ENGINE = 'sqlite3' +DATABASE_NAME = os.path.join(ROOT_PATH, 'testdb.sqlite') + +TIME_ZONE = 'America/Chicago' +LANGUAGE_CODE = 'en-us' +SITE_ID = 1 +USE_I18N = True +MEDIA_ROOT = '' +MEDIA_URL = '' +ADMIN_MEDIA_PREFIX = '/media/' +SECRET_KEY = '2+@4vnr#v8e273^+a)g$8%dre^dwcn#d&n#8+l6jk7r#$p&3zk' +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.load_template_source', + 'django.template.loaders.app_directories.load_template_source', +) +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', +) +ROOT_URLCONF = 'urls' +TEMPLATE_DIRS = (os.path.join(ROOT_PATH, 'templates'),) +INSTALLED_APPS = ( + 's3project', # ugly but easier + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', +) + +DEFAULT_FILE_STORAGE = 'backends.S3Storage.S3Storage' + +from S3 import CallingFormat +AWS_CALLING_FORMAT = CallingFormat.SUBDOMAIN +AWS_HEADERS = { + 'Expires': 'Thu, 15 Apr 2010 20:00:00 GMT', # see http://developer.yahoo.com/performance/rules.html#expires + 'Cache-Control': 'max-age=86400', + } + +# local_settings.py can be used to override environment-specific settings +# like database and email that differ between development and production. +# Add you custom AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and +# AWS_STORAGE_BUCKET_NAME settings +try: + from local_settings import * +except ImportError: + pass diff --git a/examples/s3project/tests.py b/examples/s3project/tests.py new file mode 100644 index 0000000..d849b20 --- /dev/null +++ b/examples/s3project/tests.py @@ -0,0 +1,200 @@ +""" +================= +Django S3 storage +================= + +Usage +===== + +Settings +-------- + +``DEFAULT_FILE_STORAGE`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +This setting store the path to the S3 storage class, the first part correspond +to the filepath and the second the name of the class, if you've got +``example.com`` in your ``PYTHONPATH`` and store your storage file in +``example.com/libs/storages/S3Storage.py``, the resulting setting will be:: + + DEFAULT_FILE_STORAGE = 'libs.storages.S3Storage.S3Storage' + +If you keep the same filename as in repository, it should always end with +``S3Storage.S3Storage``. + +``AWS_ACCESS_KEY_ID`` +~~~~~~~~~~~~~~~~~~~~~ + +Your Amazon Web Services access key, as a string. + +``AWS_SECRET_ACCESS_KEY`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Your Amazon Web Services secret access key, as a string. + +``AWS_STORAGE_BUCKET_NAME`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Your Amazon Web Services storage bucket name, as a string. + +``AWS_CALLING_FORMAT`` +~~~~~~~~~~~~~~~~~~~~~~ + +The way you'd like to call the Amazon Web Services API, for instance if you +prefer subdomains:: + + from S3 import CallingFormat + AWS_CALLING_FORMAT = CallingFormat.SUBDOMAIN + +``AWS_HEADERS`` (optionnal) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you'd like to set headers sent with each file of the storage:: + + # see http://developer.yahoo.com/performance/rules.html#expires + AWS_HEADERS = { + 'Expires': 'Thu, 15 Apr 2010 20:00:00 GMT', + 'Cache-Control': 'max-age=86400', + } + + +Fields +------ + +Once you're done, ``default_storage`` will be the S3 storage:: + + >>> from django.core.files.storage import default_storage + >>> print default_storage.__class__ + <class 'backends.S3Storage.S3Storage'> + +This way, if you define a new ``FileField``, it will use the S3 storage:: + + >>> from django.db import models + >>> class Resume(models.Model): + ... pdf = models.FileField(upload_to='pdfs') + ... photos = models.ImageField(upload_to='photos') + ... + >>> resume = Resume() + >>> print resume.pdf.storage + <backends.S3Storage.S3Storage object at ...> + + +Tests +===== + +Initialization:: + + >>> from django.core.files.storage import default_storage + >>> from django.core.files.base import ContentFile + >>> from django.core.cache import cache + >>> from models import MyStorage + +Storage +------- + +Standard file access options are available, and work as expected:: + + >>> default_storage.exists('storage_test') + False + >>> file = default_storage.open('storage_test', 'w') + >>> file.write('storage contents') + >>> file.close() + + >>> default_storage.exists('storage_test') + True + >>> file = default_storage.open('storage_test', 'r') + >>> file.read() + 'storage contents' + >>> file.close() + + >>> default_storage.delete('storage_test') + >>> default_storage.exists('storage_test') + False + +Model +----- + +An object without a file has limited functionality:: + + >>> obj1 = MyStorage() + >>> obj1.normal + <FieldFile: None> + >>> obj1.normal.size + Traceback (most recent call last): + ... + ValueError: The 'normal' attribute has no file associated with it. + +Saving a file enables full functionality:: + + >>> obj1.normal.save('django_test.txt', ContentFile('content')) + >>> obj1.normal + <FieldFile: tests/django_test.txt> + >>> obj1.normal.size + 7 + >>> obj1.normal.read() + 'content' + +Files can be read in a little at a time, if necessary:: + + >>> obj1.normal.open() + >>> obj1.normal.read(3) + 'con' + >>> obj1.normal.read() + 'tent' + >>> '-'.join(obj1.normal.chunks(chunk_size=2)) + 'co-nt-en-t' + +Save another file with the same name:: + + >>> obj2 = MyStorage() + >>> obj2.normal.save('django_test.txt', ContentFile('more content')) + >>> obj2.normal + <FieldFile: tests/django_test_.txt> + >>> obj2.normal.size + 12 + +Push the objects into the cache to make sure they pickle properly:: + + >>> cache.set('obj1', obj1) + >>> cache.set('obj2', obj2) + >>> cache.get('obj2').normal + <FieldFile: tests/django_test_.txt> + +Deleting an object deletes the file it uses, if there are no other objects +still using that file:: + + >>> obj2.delete() + >>> obj2.normal.save('django_test.txt', ContentFile('more content')) + >>> obj2.normal + <FieldFile: tests/django_test_.txt> + +Default values allow an object to access a single file:: + + >>> obj3 = MyStorage.objects.create() + >>> obj3.default + <FieldFile: tests/default.txt> + >>> obj3.default.read() + 'default content' + +But it shouldn't be deleted, even if there are no more objects using it:: + + >>> obj3.delete() + >>> obj3 = MyStorage() + >>> obj3.default.read() + 'default content' + +Verify the fix for #5655, making sure the directory is only determined once:: + + >>> obj4 = MyStorage() + >>> obj4.random.save('random_file', ContentFile('random content')) + >>> obj4.random + <FieldFile: .../random_file> + +Clean up the temporary files:: + + >>> obj1.normal.delete() + >>> obj2.normal.delete() + >>> obj3.default.delete() + >>> obj4.random.delete() + +"""
\ No newline at end of file |
