File uploads to Amazon S3 in Django

├── base │   ├── __init__.py │   ├── local_settings.py │   ├── settings.py │   ├── urls.py │   └── wsgi.py ├── candidate │   ├── admin.py │   ├── fixtures │   │   └── gradient.jpg │   ├── forms.py │   ├── __init__.py │   ├── models.py │   ├── tests.py │   └── views.py ├── manage.py ├── README.md ├── requirements.txt └── templates ├── candidate.html └── thanks.html I use boto to perform the I/O between my server and Amazon S3..It requires an access key id, secret access key and bucket name..Ive kept these out of settings.py and instead placed them in local_settings.py which is excluded from the git repo..I also took SECRET_KEY out of the boilerplate settings.py Django generates and placed it in local_settings.py as well..$ cat base/local_settings.py AWS_S3_ACCESS_KEY_ID = 'Get this from Amazon' AWS_S3_SECRET_ACCESS_KEY = 'This as well' AWS_STORAGE_BUCKET_NAME = 'your-s3-bucket-name' SECRET_KEY = 'A long string with many different types of characters' In settings.py I made the following additions to the boilerplate file: import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) TEMPLATE_DIRS = os.path.join(BASE_DIR, 'templates') INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'storages', 'candidate', ) DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' And, at the very bottom of settings.py I added the following in: try: from local_settings import * except ImportError: pass assert len(SECRET_KEY) > 20, 'Please set SECRET_KEY in local_settings.py' Writing the application code I wrote a model called Candidate, created a form for it and used those in a view method: candidate/models.py: from django.db import models class Candidate(models.Model): first_name = models.CharField(max_length=30) photo = models.FileField(upload_to='candidate-photos') candidate/forms.py: from django.forms import ModelForm from candidate.models import Candidate class CandidateForm(ModelForm): class Meta: model = Candidate candidate/views.py: from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect from django.shortcuts import render from candidate.forms import CandidateForm def create(request): if request.method == 'POST': form = CandidateForm(request.POST, request.FILES) if form.is_valid(): form.save() return HttpResponseRedirect(reverse('created')) else: form = CandidateForm() return render(request, 'candidate.html', { 'form': form, }) def created(request): return render(request, 'thanks.html') Normally I create a urls.py for each application in a Django project but since this was a 2-view project I just used the main base/urls.py file to configure the two endpoints: base/urls.py: from django.conf.urls import patterns, url urlpatterns = patterns('', url(r'^$', 'candidate.views.create', name='create'), url(r'^thanks/', 'candidate.views.created', name='created'), ) Testing uploads With the application built I wanted to write a test to make sure the form will accept a file, a first name and save them..I created a small JPEG file and saved it to candidate/fixtures/gradient.jpg..This will be the example image I upload when Im testing the form..The default file storage class in settings.py is S3BotoStorage..This class will upload any files to whichever cloud provider configured..I dont want to upload to the cloud when Im running my tests so Ive patched S3BotoStorage with FileSystemStorage..But by using FileSystemStorage Ill end up with any files uploaded during testing being stored in my Django projects folder..To avoid this littering Ill create a temporary folder, save the files there and then remove that folder when testing is complete.. More details

Leave a Reply