Changes between Version 7 and Version 8 of AmazonSimpleStorageService


Ignore:
Timestamp:
Jun 3, 2010, 3:51:50 PM (14 years ago)
Author:
Adam Nelson
Comment:

Super old code replaced by link to django-storages

Legend:

Unmodified
Added
Removed
Modified
  • AmazonSimpleStorageService

    v7 v8  
    1 
    2 Here's a class I created to integrate my site with amazon's storage service.
    3 
    4 I like this because in the admin interface it looks pretty much like a file field and you can just click to upload.  In theory you could integrate it into any oldforms system.
    5 
    6 You can set a default bucket value for the entire system into your settings module or for specific fields.  You can manually specify a key or let the class generate one for you.
    7 
    8 This hasn't been tested extensively, so I invite you to contribute any fixes right here on the page.  Maybe the django gods will see fit to include this right into django.
    9 
    10 Call get_WHATEVER_url() on the object to get the public URL to view/download the given file.
    11 
    12 Currently if you change the content type in the object, that change doesn't take effect until the next time you upload a file.
    13 
    14 {{{
    15 #!python
    16 from amazon import S3
    17 from django.conf import settings
    18 from django.db import models
    19 from mimetypes import guess_type
    20 from django.core import validators
    21 from django import oldforms
    22 from django.dispatch import dispatcher
    23 from django.utils.functional import curry
    24 from django.utils.translation import gettext_lazy
    25 from django.db.models import signals
    26 from PIL import Image
    27 from StringIO import StringIO
    28 import os
    29 
    30 conn = S3.AWSAuthConnection(settings.AWS_ACCESS_KEY_ID,
    31                             settings.AWS_SECRET_ACCESS_KEY)
    32 generator = S3.QueryStringAuthGenerator(settings.AWS_ACCESS_KEY_ID,
    33                                         settings.AWS_SECRET_ACCESS_KEY)
    34 
    35 class S3FileField(models.FileField):
    36     def __init__(self, verbose_name=None, name=None, bucket='', is_image=False, **kwargs):
    37         models.FileField.__init__(self, verbose_name, name, upload_to="s3", **kwargs)
    38         self.bucket = bucket
    39         self.is_image = is_image
    40 
    41     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    42         field_list = models.Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
    43         if not self.blank:
    44             if rel:
    45                 # This validator makes sure FileFields work in a related context.
    46                 class RequiredFileField(object):
    47                     def __init__(self, other_field_names, other_file_field_name):
    48                         self.other_field_names = other_field_names
    49                         self.other_file_field_name = other_file_field_name
    50                         self.always_test = True
    51                     def __call__(self, field_data, all_data):
    52                         if not all_data.get(self.other_file_field_name, False):
    53                             c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, gettext_lazy("This field is required."))
    54                             c(field_data, all_data)
    55                 # First, get the core fields, if any.
    56                 core_field_names = []
    57                 for f in opts.fields:
    58                     if f.core and f != self:
    59                         core_field_names.extend(f.get_manipulator_field_names(name_prefix))
    60                 # Now, if there are any, add the validator to this FormField.
    61                 if core_field_names:
    62                     field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
    63             else:
    64                 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, gettext_lazy("This field is required."))
    65                 v.always_test = True
    66                 field_list[0].validator_list.append(v)
    67                 field_list[0].is_required = field_list[1].is_required = False
    68 
    69         return field_list
    70    
    71     def get_internal_type(self):
    72         return "FileField"
    73    
    74     def contribute_to_class(self, cls, name):
    75         super(S3FileField, self).contribute_to_class(cls, name)
    76         models.CharField(maxlength=200, blank=self.blank, null=self.null).contribute_to_class(cls, "%s_key"%(self.name))
    77         models.CharField(maxlength=200, blank=self.blank, null=self.null, default=(self.bucket or settings.DEFAULT_BUCKET)).contribute_to_class(cls, "%s_bucket"%(self.name))
    78         models.CharField(maxlength=200, blank=True, null=True, default="").contribute_to_class(cls, "%s_content_type"%(self.name))
    79         models.IntegerField(blank=True, null=True).contribute_to_class(cls, "%s_size"%(self.name))
    80         if self.is_image:
    81             models.IntegerField(blank=True, null=True).contribute_to_class(cls, "%s_width"%(self.name))
    82             models.IntegerField(blank=True, null=True).contribute_to_class(cls, "%s_height"%(self.name))
    83        
    84         # Getter for the file url
    85         def get_url(instance, field):
    86             return field.get_url(instance)
    87         setattr(cls, 'get_%s_url' % self.name, curry(get_url, field=self))
    88        
    89         dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
    90 
    91     def delete_file(self, instance):
    92         if getattr(instance, self.attname):
    93             bucket = self.get_bucket(instance)
    94             key = self.get_key(instance)
    95             conn.delete(bucket, key)
    96    
    97     def get_url(self, instance):
    98         bucket = self.get_bucket(instance)
    99         key = self.get_key(instance)
    100         if bucket and key:
    101             url = generator.make_bare_url(bucket, key)
    102             return url
    103         return None
    104                
    105     def get_bucket(self, instance):
    106         return getattr(instance, "%s_bucket"%self.name)
    107    
    108     def set_bucket(self, instance, bucket):   
    109         setattr(instance, "%s_bucket"%self.name, bucket)
    110        
    111     def get_key(self, instance):
    112         return getattr(instance, "%s_key"%self.name)
    113    
    114     def set_key(self, instance, key):   
    115         setattr(instance, "%s_key"%self.name, key)
    116        
    117     def get_content_type(self, instance):
    118         return getattr(instance, "%s_content_type"%self.name)
    119        
    120     def set_content_type(self, instance, content_type):   
    121         setattr(instance, "%s_content_type"%self.name, content_type)
    122        
    123     def get_size(self, instance):
    124         return getattr(instance, "%s_size"%self.name)
    125        
    126     def set_size(self, instance, size):   
    127         setattr(instance, "%s_size"%self.name, size)
    128        
    129     def get_filename(self, instance):
    130         return getattr(instance, self.name)
    131        
    132     def set_filename(self, instance, filename):   
    133         setattr(instance, self.name, filename)
    134        
    135     def get_width(self, instance):
    136         return getattr(instance, "%s_width"%self.name)
    137    
    138     def set_width(self, instance, width):   
    139         setattr(instance, "%s_width"%self.name, width)
    140        
    141     def get_height(self, instance):
    142         return getattr(instance, "%s_height"%self.name)
    143        
    144     def set_height(self, instance, height):   
    145         setattr(instance, "%s_height"%self.name, height)
    146                
    147     def get_manipulator_field_objs(self):
    148         if self.is_image: uploadType = oldforms.ImageUploadField
    149         else: uploadType = oldforms.FileUploadField
    150         return [uploadType, oldforms.HiddenField]
    151 
    152     def get_manipulator_field_names(self, name_prefix):
    153         return [name_prefix + self.name + '_file', name_prefix + self.name]
    154 
    155     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
    156         upload_field_name = self.get_manipulator_field_names('')[0]
    157         if new_data.get(upload_field_name, False):
    158             if rel:
    159                 new_content = new_data[upload_field_name][0]["content"]
    160                 new_filename = new_data[upload_field_name][0]["filename"]
    161             else:
    162                 new_content = new_data[upload_field_name]["content"]
    163                 new_filename = new_data[upload_field_name]["filename"]
    164            
    165             self.set_filename(new_object, new_filename)
    166             self.set_size(new_object, len(new_content))
    167            
    168             key = new_data["%s_key"%self.name]
    169             if not key:
    170                 key = "_".join((new_object.__class__.__name__.lower(), str(new_object.id), self.name))
    171                 self.set_key(new_object, key)
    172             bucket = new_data["%s_bucket"%self.name]
    173             if not bucket:
    174                 bucket = self.bucket
    175             content_type = new_data["%s_content_type"%self.name]
    176             if new_filename and not content_type:
    177                 content_type = guess_type(new_filename)[0]
    178                 if not content_type:
    179                     content_type = "application/x-octet-stream"
    180             conn.put(bucket, key, S3.S3Object(new_content),
    181                      { 'x-amz-acl': 'public-read' , 'Content-Type': content_type})
    182            
    183             if self.is_image:
    184                 # Calculate image width/height
    185                 img = Image.open(StringIO(new_content))
    186                 width, height = img.size
    187                 self.set_width(new_object, width)
    188                 self.set_height(new_object, height)
    189 }}}
     1[http://bitbucket.org/david/django-storages/wiki/Home Django Storages] using S3 Boto is the best solution for using Amazon S3 as a custom file storage backend in Django.
Back to Top