Commits

David Larlet committed 5851d7e

S3: support for latest filestorage patch + tests, no support for chuncked files for now

Comments (0)

Files changed (5)

+import os
 from mimetypes import guess_type
-import os
 
+from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
-from django.core.filestorage.base import Storage, RemoteFile
-from django.core.filestorage.filesystem import FileSystemStorage
+from django.core.files.storage import Storage
+from django.core.files.remote import RemoteFile
 from django.utils.functional import curry
-from django.conf import settings
 
 ACCESS_KEY_NAME = 'AWS_ACCESS_KEY_ID'
 SECRET_KEY_NAME = 'AWS_SECRET_ACCESS_KEY'
     raise ImproperlyConfigured, "Could not load amazon's S3 bindings.\
     \nSee http://developer.amazonwebservices.com/connect/entry.jspa?externalID=134"
 
+
 class S3Storage(Storage):
     """Amazon Simple Storage Service"""
 
     def _get_connection(self):
         return AWSAuthConnection(*self._get_access_keys())
 
-    def _put_file(self, filename, raw_contents):
-        content_type = guess_type(filename)[0] or "application/x-octet-stream"
+    def _put_file(self, name, raw_contents):
+        content_type = guess_type(name)[0] or "application/x-octet-stream"
         self.headers.update({'x-amz-acl':  self.acl, 'Content-Type': content_type})
-        response = self.connection.put(self.bucket, filename, raw_contents, self.headers)
+        response = self.connection.put(self.bucket, name, raw_contents, self.headers)
 
-    def url(self, filename):
-        return self.generator.make_bare_url(self.bucket, filename)
+    def path(self, name):
+        return self.generator.make_bare_url(self.bucket, name)
     
-    path = url
-
-    def filesize(self, filename):
-        response = self.connection.make_request('HEAD', self.bucket, filename)
+    def size(self, name):
+        response = self.connection._make_request('HEAD', self.bucket, name)
         return int(response.getheader('Content-Length'))
-
-    def open(self, filename, mode='rb'):
-        response = self.connection.get(self.bucket, filename)
-        writer = curry(self._put_file, filename)
-        return RemoteFile(self, response.object.data, mode, writer)
-
-    def exists(self, filename):
-        response = self.connection.make_request('HEAD', self.bucket, filename)
+    
+    url = path
+    
+    def exists(self, name):
+        response = self.connection._make_request('HEAD', self.bucket, name)
         return response.status == 200
 
-    def save(self, filename, raw_contents):
-        filename = self.get_available_filename(filename)
-        self._put_file(filename, raw_contents)
-        return filename
+    def _open(self, name, mode='rb'):
+        response = self.connection.get(self.bucket, name)
+        writer = curry(self._put_file, name)
+        return RemoteFile(response.object.data, mode, writer)
+
+    def save(self, name, raw_contents):
+        name = self.get_available_filename(name)
+        self._put_file(name, raw_contents)
+        return name
     
+    def delete(self, name):
+        self.connection.delete(self.bucket, name)
+
     ## UNCOMMENT BELOW IF NECESSARY
-    
-    #def delete(self, filename):
-    #    """ Do not delete default images. """
-    #    if not filename.endswith('default.jpg') and not filename.endswith('guest.jpg'):
-    #        self.connection.delete(self.bucket, filename)
-
-    #def get_available_filename(self, filename):
+    #def get_available_filename(self, name):
     #    """ Overwrite existing file with the same name. """
-    #    return filename
+    #    return name

storages_tests/__init__.py

Empty file added.

storages_tests/manage.py

+# 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)

storages_tests/models.py

+from django.db import models
+from django.core.files.storage import default_storage
+
+s3_storage = default_storage
+
+# Write out a file to be used as default content
+s3_storage.save('tests/default.txt',  'default content')
+
+
+class Storage(models.Model):
+    def custom_upload_to(self, filename):
+        return 'foo'
+
+    normal = models.FileField(storage=s3_storage, upload_to='tests')
+    custom = models.FileField(storage=s3_storage, upload_to=custom_upload_to)
+    default = models.FileField(storage=s3_storage, upload_to='tests', default='tests/default.txt')

storages_tests/tests.py

+"""
+=================
+Django S3 storage
+=================
+
+Initialization::
+
+    >>> from django.core.files.remote import RemoteFile
+    >>> from django.core.files.storage import default_storage
+    >>> from django.core.cache import cache
+    >>> from models import Storage
+
+An object without a file has limited functionality::
+
+    >>> obj1 = Storage()
+    >>> 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', 'content')
+    >>> obj1.normal
+    <FieldFile: tests/django_test.txt>
+    >>> obj1.normal.size
+    7
+    >>> obj1.normal.read()
+    'content'
+
+Save another file with the same name::
+
+    >>> obj2 = Storage()
+    >>> obj2.normal.save('django_test.txt', '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', 'more content')
+    >>> obj2.normal
+    <FieldFile: tests/django_test_.txt>
+
+Default values allow an object to access a single file::
+
+    >>> obj3 = Storage.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 = Storage()
+    >>> obj3.default.read()
+    'default content'
+
+Clean up the temporary files::
+
+    >>> obj1.normal.delete()
+    >>> obj2.normal.delete()
+    >>> obj3.default.delete()
+"""