Skip to content

Commit ef8d5ea

Browse files
Merge pull request #13 from maxicecilia/master
Migrate to Dropbox API v2
2 parents 9b06bb2 + ee0021b commit ef8d5ea

File tree

11 files changed

+103
-96
lines changed

11 files changed

+103
-96
lines changed

README.md

+24-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# django-dropbox
2-
> Version 0.0.1
2+
> Version 0.1.2
33
44
# What
55

@@ -23,16 +23,31 @@ INSTALLED_APPS on settings.py
2323

2424
additionally you must need to set the next settings:
2525

26-
DROPBOX_CONSUMER_KEY = 'xxx'
27-
DROPBOX_CONSUMER_SECRET = 'xxx'
2826
DROPBOX_ACCESS_TOKEN = 'xxx'
29-
DROPBOX_ACCESS_TOKEN_SECRET = 'xxx'
3027

31-
if you don't have `DROPBOX_CONSUMER_KEY` or `DROPBOX_CONSUMER_SECRET`
32-
you will need to create an Dropbox app at [Dropbox for Developers](https://www.dropbox.com/developers)
33-
then set `DROPBOX_CONSUMER_KEY` and `DROPBOX_CONSUMER_SECRET` settings in `settings.py`,
34-
after that run:
28+
if you don't have `DROPBOX_ACCESS_TOKEN` you can create one after creating a Dropbox app at [Dropbox for Developers](https://www.dropbox.com/developers).
29+
If you have your Dropbox `App key` and `App secret`, you can set `DROPBOX_CONSUMER_KEY` and `DROPBOX_CONSUMER_SECRET` settings in `settings.py`, then run:
3530

3631
$ python manage.py get_dropbox_token
3732

38-
And follow up on screen instructions, finally set the `DROPBOX_ACCESS_TOKEN` and `DROPBOX_ACCESS_TOKEN_SECRET` in `settings.py`
33+
And follow up on screen instructions, finally set the and `DROPBOX_ACCESS_TOKEN_SECRET` in `settings.py`
34+
35+
36+
# Contributing
37+
When contributing, please follow these steps:
38+
39+
* Clone the repo and make your changes.
40+
* Make sure your code has test cases written against it.
41+
* Make sure all the tests pass.
42+
* Lint your code with Flake8.
43+
* Add your name to the list of contributers.
44+
* Submit a Pull Request.
45+
46+
## Tests
47+
48+
Tests are written following Django best practices. You can run them all easily using the example django_project.
49+
50+
```
51+
$ cd django_dropbox_project
52+
$ python manage.py test --settings=settings
53+
```
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
from django.core.management.base import NoArgsCommand
2-
from dropbox import rest, session
3-
from django_dropbox.settings import CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TYPE
1+
from django.core.management.base import NoArgsCommand, CommandError
2+
from dropbox import DropboxOAuth2FlowNoRedirect
3+
4+
from django_dropbox.settings import CONSUMER_KEY, CONSUMER_SECRET
5+
46

57
class Command(NoArgsCommand):
68

79
def handle_noargs(self, *args, **options):
8-
sess = session.DropboxSession(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TYPE)
9-
request_token = sess.obtain_request_token()
10+
auth_flow = DropboxOAuth2FlowNoRedirect(CONSUMER_KEY, CONSUMER_SECRET)
1011

11-
url = sess.build_authorize_url(request_token)
12-
print "Url:", url
13-
print "Please visit this website and press the 'Allow' button, then hit 'Enter' here."
14-
raw_input()
15-
16-
# This will fail if the user didn't visit the above URL and hit 'Allow'
17-
access_token = sess.obtain_access_token(request_token)
12+
authorize_url = auth_flow.start()
13+
self.stdout.write('1. Go to: {}'.format(authorize_url))
14+
self.stdout.write('2. Click "Allow" (you might have to log in first).')
15+
self.stdout.write('3. Copy the authorization code.')
16+
auth_code = raw_input("Enter the authorization code here: ").strip()
1817

19-
print "DROPBOX_ACCESS_TOKEN = '%s'" % access_token.key
20-
print "DROPBOX_ACCESS_TOKEN_SECRET = '%s'" % access_token.secret
18+
try:
19+
oauth_result = auth_flow.finish(auth_code)
20+
self.stdout.write("DROPBOX_ACCESS_TOKEN = '{}'".format(oauth_result.access_token))
21+
except Exception as e:
22+
raise CommandError('Error: {}'.format(e))

django_dropbox/settings.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
ACCESS_TOKEN = getattr(settings, 'DROPBOX_ACCESS_TOKEN', None)
66
ACCESS_TOKEN_SECRET = getattr(settings, 'DROPBOX_ACCESS_TOKEN_SECRET', None)
77
CACHE_TIMEOUT = getattr(settings, 'DROPBOX_CACHE_TIMEOUT', 3600 * 24 * 365) # One year
8+
SHARE_LINK_CACHE_TIMEOUT = getattr(settings, 'DROPBOX_SHARE_LINK_CACHE_TIMEOUT', 3600 * 3)
89

910
# ACCESS_TYPE should be 'dropbox' or 'app_folder' as configured for your app
10-
ACCESS_TYPE = getattr(settings,'ACCESS_TYPE','app_folder')
11-
11+
ACCESS_TYPE = getattr(settings, 'ACCESS_TYPE', 'app_folder')

django_dropbox/storage.py

+34-43
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,33 @@
1-
import errno
21
import os.path
32
import re
4-
import urlparse
5-
import urllib
63
import itertools
74
try:
85
from cStringIO import StringIO
96
except ImportError:
107
from StringIO import StringIO
11-
from dropbox.session import DropboxSession
12-
from dropbox.client import DropboxClient
13-
from dropbox.rest import ErrorResponse
8+
from dropbox import Dropbox
9+
from dropbox.exceptions import ApiError
10+
from dropbox.files import FolderMetadata, FileMetadata
1411
from django.core.cache import cache
1512
from django.core.files import File
1613
from django.core.files.storage import Storage
14+
from django.utils.deconstruct import deconstructible
1715
from django.utils.encoding import filepath_to_uri
1816

19-
from .settings import (CONSUMER_KEY,
20-
CONSUMER_SECRET,
21-
ACCESS_TYPE,
22-
ACCESS_TOKEN,
23-
ACCESS_TOKEN_SECRET,
24-
CACHE_TIMEOUT)
17+
from .settings import ACCESS_TOKEN, CACHE_TIMEOUT, SHARE_LINK_CACHE_TIMEOUT
2518

2619

20+
@deconstructible
2721
class DropboxStorage(Storage):
2822
"""
2923
A storage class providing access to resources in a Dropbox Public folder.
3024
"""
3125

3226
def __init__(self, location='/Public'):
33-
session = DropboxSession(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TYPE, locale=None)
34-
session.set_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
35-
self.client = DropboxClient(session)
36-
self.account_info = self.client.account_info()
27+
self.client = Dropbox(ACCESS_TOKEN)
28+
self.account_info = self.client.users_get_current_account()
3729
self.location = location
38-
self.base_url = 'http://dl.dropbox.com/u/{uid}/'.format(**self.account_info)
30+
self.base_url = 'https://dl.dropboxusercontent.com/'
3931

4032
def _get_abs_path(self, name):
4133
return os.path.realpath(os.path.join(self.location, name))
@@ -49,59 +41,56 @@ def _save(self, name, content):
4941
name = self._get_abs_path(name)
5042
directory = os.path.dirname(name)
5143
if not self.exists(directory) and directory:
52-
self.client.file_create_folder(directory)
53-
response = self.client.metadata(directory)
54-
if not response['is_dir']:
55-
raise IOError("%s exists and is not a directory." % directory)
44+
self.client.files_create_folder(directory)
45+
# response = self.client.files_get_metadata(directory)
46+
# if not response['is_dir']:
47+
# raise IOError("%s exists and is not a directory." % directory)
5648
abs_name = os.path.realpath(os.path.join(self.location, name))
57-
self.client.put_file(abs_name, content)
49+
foo = self.client.files_upload(content.read(), abs_name)
5850
return name
5951

6052
def delete(self, name):
6153
name = self._get_abs_path(name)
62-
self.client.file_delete(name)
54+
self.client.files_delete(name)
6355

6456
def exists(self, name):
6557
name = self._get_abs_path(name)
6658
try:
67-
metadata = self.client.metadata(name)
68-
if metadata.get('is_deleted'):
69-
return False
70-
except ErrorResponse as e:
71-
if e.status == 404: # not found
59+
self.client.files_get_metadata(name)
60+
except ApiError as e:
61+
if e.error.is_path() and e.error.get_path().is_not_found(): # not found
7262
return False
7363
raise e
7464
return True
7565

7666
def listdir(self, path):
7767
path = self._get_abs_path(path)
78-
response = self.client.metadata(path)
68+
response = self.client.files_list_folder(path)
7969
directories = []
8070
files = []
81-
for entry in response.get('contents', []):
82-
if entry['is_dir']:
83-
directories.append(os.path.basename(entry['path']))
84-
else:
85-
files.append(os.path.basename(entry['path']))
71+
for entry in response.entries:
72+
if type(entry) == FolderMetadata:
73+
directories.append(os.path.basename(entry.path_display))
74+
elif type(entry) == FileMetadata:
75+
files.append(os.path.basename(entry.path_display))
8676
return directories, files
8777

8878
def size(self, name):
89-
cache_key = 'django-dropbox-size:%s' % filepath_to_uri(name)
79+
cache_key = 'django-dropbox-size:{}'.format(filepath_to_uri(name))
9080
size = cache.get(cache_key)
9181

9282
if not size:
93-
size = self.client.metadata(filepath_to_uri(name))['bytes']
83+
size = self.client.files_get_metadata(name).size
9484
cache.set(cache_key, size, CACHE_TIMEOUT)
95-
9685
return size
9786

9887
def url(self, name):
99-
cache_key = 'django-dropbox-url:%s' % filepath_to_uri(name)
88+
cache_key = 'django-dropbox-size:{}'.format(filepath_to_uri(name))
10089
url = cache.get(cache_key)
10190

10291
if not url:
103-
url = self.client.share(filepath_to_uri(name), short_url=False)['url'] + '?dl=1'
104-
cache.set(cache_key, url, CACHE_TIMEOUT)
92+
url = self.client.files_get_temporary_link(name).link
93+
cache.set(cache_key, url, SHARE_LINK_CACHE_TIMEOUT)
10594

10695
return url
10796

@@ -123,6 +112,7 @@ def get_available_name(self, name):
123112

124113
return name
125114

115+
126116
class DropboxFile(File):
127117
def __init__(self, name, storage, mode):
128118
self._storage = storage
@@ -139,7 +129,8 @@ def size(self):
139129
return self._size
140130

141131
def read(self, num_bytes=None):
142-
return self._storage.client.get_file(self._name).read()
132+
metadata, response = self._storage.client.files_download(self._name)
133+
return response.content
143134

144135
def write(self, content):
145136
if 'w' not in self._mode:
@@ -149,5 +140,5 @@ def write(self, content):
149140

150141
def close(self):
151142
if self._is_dirty:
152-
self._storage.client.put_file(self._name, self.file.getvalue())
153-
self.file.close()
143+
self._storage.client.files_upload(self.file.getvalue(), self._name)
144+
self.file.close()

django_dropbox/tests.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
#import os
1+
# import os
22
from django.core.files.base import ContentFile
33
from django.test import TestCase
44
from django_dropbox.storage import DropboxStorage
55

6+
67
class DropboxStorageTest(TestCase):
78

89
def setUp(self):
@@ -29,7 +30,7 @@ def test_file_access_options(self):
2930

3031
def test_exists_folder(self):
3132
self.assertFalse(self.storage.exists('storage_test_exists'))
32-
self.storage.client.file_create_folder(self.location + '/storage_test_exists')
33+
self.storage.client.files_create_folder(self.location + '/storage_test_exists')
3334
self.assertTrue(self.storage.exists('storage_test_exists'))
3435
self.storage.delete('storage_test_exists')
3536
self.assertFalse(self.storage.exists('storage_test_exists'))
@@ -44,7 +45,7 @@ def test_listdir(self):
4445

4546
f = self.storage.save('storage_test_1', ContentFile('custom content'))
4647
f = self.storage.save('storage_test_2', ContentFile('custom content'))
47-
self.storage.client.file_create_folder(self.location + '/storage_dir_1')
48+
self.storage.client.files_create_folder(self.location + '/storage_dir_1')
4849

4950
dirs, files = self.storage.listdir(self.location)
5051
self.assertEqual(set(dirs), set([u'storage_dir_1']))
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from django.contrib import admin
22
from dropbox_testing.models import Person
33

4+
45
class PersonAdmin(admin.ModelAdmin):
56
list_display = ('image',)
6-
7+
78
def image(self, obj):
89
if obj.photo:
910
return '<img src="%s">' % obj.photo.url
1011
return ''
1112
image.allow_tags = True
1213

13-
admin.site.register(Person, PersonAdmin)
14+
admin.site.register(Person, PersonAdmin)

django_dropbox_project/dropbox_testing/models.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
STORAGE = DropboxStorage()
55

6+
67
class Person(models.Model):
7-
photo = models.ImageField(upload_to='photos', storage=STORAGE, null=True, blank=True)
8-
resume = models.FileField(upload_to='resumes', storage=STORAGE, null=True, blank=True)
8+
photo = models.ImageField(upload_to='photos', storage=STORAGE, null=True, blank=True)
9+
resume = models.FileField(upload_to='resumes', storage=STORAGE, null=True, blank=True)

django_dropbox_project/manage.py

+5-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
#!/usr/bin/env python
2-
from django.core.management import execute_manager
3-
import imp
4-
try:
5-
imp.find_module('settings') # Assumed to be in the same directory.
6-
except ImportError:
7-
import sys
8-
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" % __file__)
9-
sys.exit(1)
2+
import os
3+
import sys
104

11-
import settings
125

136
if __name__ == "__main__":
14-
execute_manager(settings)
7+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
8+
from django.core.management import execute_from_command_line
9+
execute_from_command_line(sys.argv)

django_dropbox_project/settings.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,5 @@
149149
try:
150150
from local_settings import *
151151
except ImportError:
152-
raise ImportError('You must add local_settings.py file')
152+
pass
153+
# raise ImportError('You must add local_settings.py file')

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
dropbox>=2.0.0
1+
dropbox>=7.3.1

setup.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django_dropbox import version
44
from setuptools import setup
55

6+
67
def get_packages():
78
# setuptools can't do the job :(
89
packages = []
@@ -12,14 +13,13 @@ def get_packages():
1213

1314
return packages
1415

15-
requires = ['dropbox>=2.0.0']
16+
requires = ['dropbox==7.3.1']
1617

1718
setup(name='django-dropbox',
18-
version=version,
19-
description='A Django App that contains a Django Storage which uses Dropbox.',
20-
author=u'Andres Torres Marroquin',
21-
author_email='[email protected]',
22-
url='https://github.com/andres-torres-marroquin/django-dropbox',
23-
packages=get_packages(),
24-
install_requires=requires,
25-
)
19+
version=version,
20+
description='A Django App that contains a Django Storage which uses Dropbox.',
21+
author=u'Andres Torres Marroquin',
22+
author_email='[email protected]',
23+
url='https://github.com/andres-torres-marroquin/django-dropbox',
24+
packages=get_packages(),
25+
install_requires=requires,)

0 commit comments

Comments
 (0)