Skip to content

Commit

Permalink
Added Dropbox storage
Browse files Browse the repository at this point in the history
  • Loading branch information
ZuluPro committed Aug 29, 2015
1 parent 2a5f93b commit 0f6bb2a
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 17 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ By order of apparition, thanks:
* Andrei Coman (Azure)
* Chris Streeter (S3 with Boto)
* Josh Schneier (Fork maintainer, Bugfixes, Py3K)
* Anthony Monthe (Dropbox)

Extra thanks to Marty for adding this in Django,
you can buy his very interesting book (Pro Django).
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
include AUTHORS CHANGELOG.rst LICENSE README.rst
include AUTHORS CHANGELOG.rst LICENSE README.rst requirements*.txt
recursive-include tests *
recursive-include docs *
5 changes: 5 additions & 0 deletions requirements-tests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Django>=1.6.2
pytest==2.6.4
boto>=2.32.0
dropbox>=3.24
mock
13 changes: 5 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from setuptools import setup

import storages


def read(filename):
with open(filename) as f:
return f.read()

def get_requirements_tests():
with open('requirements-tests.txt') as f:
return f.readlines()

setup(
name='django-storages-redux',
version=storages.__version__,
Expand All @@ -31,13 +34,7 @@ def read(filename):
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
],
tests_require=[
'Django>=1.6.2',
'pytest',
'mock',
'boto>=2.32.0',
'dropbox>=2.2.0'
],
tests_require=get_requirements_tests(),
test_suite='tests',
zip_safe=False
)
91 changes: 91 additions & 0 deletions storages/backends/dropbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Dropbox storage class for Django pluggable storage system.
# Author: Anthony Monthe <anthony.monthe@gmail.com>
# License: BSD
#
# Usage:
#
# Add below to settings.py:
# DROPBOX_OAUTH2_TOKEN = 'YourOauthToken'

from __future__ import absolute_import

from datetime import datetime

from django.core.files.base import File
from django.core.exceptions import ImproperlyConfigured

from storages.compat import BytesIO, Storage
from storages.utils import setting

from dropbox.client import DropboxClient

DATE_FORMAT = '%a, %d %b %Y %X +0000'


class DropBoxStorageException(Exception):
pass


class DropBoxFile(File):
def __init__(self, name, storage):
self.name = name
self._storage = storage

def read(self, num_bytes=None):
self._storage._read(self.name, num_bytes=num_bytes)

def write(self, content):
self._storage._save(self.name, content)


class DropBoxStorage(Storage):
"""DropBox Storage class for Django pluggable storage system."""

def __init__(self, oauth2_access_token=setting('DROPBOX_OAUTH2_TOKEN')):
if oauth2_access_token is None:
raise ImproperlyConfigured("You must configure a token auth at"
"'settings.DROPBOX_OAUTH2_TOKEN'.")
self.client = DropboxClient(oauth2_access_token)

def delete(self, name):
self.client.file_delete(name)

def exists(self, name):
response = self.client.search('/', name, file_limit=1)
return bool(response)

def listdir(self, path):
directories, files = [], []
metadata = self.client.metadata(path)
for entry in metadata['contents']:
if entry['is_dir']:
directories.append(entry['path'])
else:
files.append(entry['path'])
return directories, files

def size(self, name):
metadata = self.client.metadata(name)
return metadata['bytes']

def modified_time(self, name):
metadata = self.client.metadata(name)
mod_time = datetime.strptime(metadata['modified'], DATE_FORMAT)
return mod_time

def accessed_time(self, name):
metadata = self.client.metadata(name)
acc_time = datetime.strptime(metadata['client_mtime'], DATE_FORMAT)
return acc_time

def _open(self, name):
remote_file = DropBoxFile(name, self)
return remote_file

def _save(self, name, content):
self.client.put_file(name, BytesIO(content))
return name

def _read(self, name, num_bytes=None):
data = self.client.get_file(name)
return data.read(num_bytes)
13 changes: 6 additions & 7 deletions tests/test_dropbox.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import re
from io import BytesIO
from datetime import datetime
try:
from unittest import mock
except ImportError: # Python 3.2 and below
import mock

from django.test import TestCase
from django.core.files.base import File
from django.core.files.base import File, ContentFile

from storages.backends import dropbox

FILE_DATE = datetime(2015, 8, 24, 15, 06, 41)
FILE_DATE = datetime(2015, 8, 24, 15, 6, 41)
FILE_FIXTURE = {
'bytes': 4,
'client_mtime': 'Mon, 24 Aug 2015 15:06:41 +0000',
Expand Down Expand Up @@ -114,10 +113,10 @@ def test_open(self, *args):
@mock.patch('dropbox.client.DropboxClient.put_file',
return_value='foo')
def test_save(self, *args):
self.storage._save('foo', 'bar')
self.storage._save('foo', b'bar')

@mock.patch('dropbox.client.DropboxClient.get_file',
return_value=BytesIO('bar'))
return_value=ContentFile('bar'))
def test_read(self, *args):
content = self.storage._read('foo')
self.assertEqual(content, 'bar')
Expand All @@ -134,10 +133,10 @@ def setUp(self, *args):
@mock.patch('dropbox.client.DropboxClient.put_file',
return_value='foo')
def test_write(self, *args):
self.storage._save('foo', 'bar')
self.storage._save('foo', b'bar')

@mock.patch('dropbox.client.DropboxClient.get_file',
return_value=BytesIO('bar'))
return_value=ContentFile('bar'))
def test_read(self, *args):
content = self.storage._read('foo')
self.assertEqual(content, 'bar')
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ deps =
py27: mock==1.0.1
boto>=2.32.0
pytest==2.6.4
dropbox>=2.2.0
dropbox>=3.24

0 comments on commit 0f6bb2a

Please sign in to comment.