Repository: jschneier/django-storages

Issue:
NotImplementedError when trying to use Google Cloud Storage
I'm trying to setup django-storages for GCS. As a test bed, I'm using django's "poll tutorial" with default admin interface. 

In my config file I have:

```
INSTALLED_APPS = (
    'storages',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls',
)
...
STATIC_URL = 'https://storage.googleapis.com/my-static/'
DEFAULT_FILE_STORAGE = 'storages.backends.apache_libcloud.LibCloudStorage'
STATICFILES_STORAGE = 'storages.backends.apache_libcloud.LibCloudStorage'

LIBCLOUD_PROVIDERS = {
    'google_cloud_storage': {
        'type': 'libcloud.storage.types.Provider.GOOGLE_STORAGE',
        'user': os.environ.get('GOOGLE_ACCESS_KEY'),
        'key': os.environ.get('GOOGLE_SECRET_KEY'),
        'bucket': 'my-static',
        'secure': True,
    },
}

DEFAULT_LIBCLOUD_PROVIDER = 'google_cloud_storage'
```

When I call
`python3 manage.py collectstatic`
everything seems to work and files are uploaded into GCS bucket.
But when I try to view the web page, I get:

```
NotImplementedError at /admin/login/
get_object_cdn_url not implemented for this driver
Request Method: GET
Request URL:    http://localhost:8080/admin/login/?next=/admin/
Django Version: 1.8.3
Exception Type: NotImplementedError
Exception Value:    
get_object_cdn_url not implemented for this driver
Exception Location: /usr/local/lib/python3.4/dist-packages/libcloud/storage/base.py in get_object_cdn_url, line 296
Python Executable:  /usr/local/bin/uwsgi
Python Version: 3.4.0
Python Path:    
['.',
 '',
 '/usr/src/app/src/django-storages',
 '/usr/lib/python3.4',
 '/usr/lib/python3.4/plat-x86_64-linux-gnu',
 '/usr/lib/python3.4/lib-dynload',
 '/usr/local/lib/python3.4/dist-packages',
 '/usr/lib/python3/dist-packages']
Server time:    Tue, 28 Jul 2015 16:33:57 +0000
Error during template rendering

In template /usr/local/lib/python3.4/dist-packages/django/contrib/admin/templates/admin/base.html, error at line 6
get_object_cdn_url not implemented for this driver
1   {% load i18n admin_static %}<!DOCTYPE html>
2   {% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
3   <html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
4   <head>
5   <title>{% block title %}{% endblock %}</title>
6   
      <link rel="stylesheet" type="text/css" href="{% block stylesheet %}
      {% static "admin/css/base.css" %}
      {% endblock %}" />
```



Discussion:
This seems to be a fix to this same issue in another library:

https://github.com/pydanny/dj-libcloud/commit/8663cb9567ab4e9c87ffc4a57991490ccae7cfb9


Resolving patch:
diff --git a/storages/backends/apache_libcloud.py b/storages/backends/apache_libcloud.py
--- a/storages/backends/apache_libcloud.py
+++ b/storages/backends/apache_libcloud.py
@@ -7,6 +7,7 @@
 from django.core.files.base import File
 from django.core.exceptions import ImproperlyConfigured
 from django.utils.six import string_types
+from django.utils.six.moves.urllib.parse import urljoin
 
 from storages.compat import BytesIO, deconstructible, Storage
 
@@ -119,8 +120,26 @@ def size(self, name):
         return obj.size if obj else -1
 
     def url(self, name):
+        provider_type = self.provider['type'].lower()
         obj = self._get_object(name)
-        return self.driver.get_object_cdn_url(obj)
+        if not obj:
+            return None
+        try:
+            url = self.driver.get_object_cdn_url(obj)
+        except NotImplementedError as e:
+            object_path = '%s/%s' % (self.bucket, obj.name)
+            if 's3' in provider_type:
+                base_url = 'https://%s' % self.driver.connection.host
+                url = urljoin(base_url, object_path)
+            elif 'google' in provider_type:
+                url = urljoin('https://storage.googleapis.com', object_path)
+            elif 'azure' in provider_type:
+                base_url = ('https://%s.blob.core.windows.net' %
+                            self.provider['user'])
+                url = urljoin(base_url, object_path)
+            else:
+                raise e
+        return url
 
     def _open(self, name, mode='rb'):
         remote_file = LibCloudFile(name, self, mode=mode)


Test patch: