Giter Site home page Giter Site logo

klis87 / django-cloudinary-storage Goto Github PK

View Code? Open in Web Editor NEW
125.0 9.0 26.0 1.1 MB

Django package that provides Cloudinary storages for both media and static files as well as management commands for removing unnecessary files.

License: MIT License

Python 99.16% CSS 0.05% HTML 0.79%
python django cloudinary static-files

django-cloudinary-storage's Introduction

Django Cloudinary Storage

Django Cloudinary Storage is a Django package that facilitates integration with Cloudinary by implementing Django Storage API. With several lines of configuration, you can start using Cloudinary for both media and static files. Also, it provides management commands for removing unnecessary files, so any cleanup will be a breeze. It uses pycloudinary package under the hood.

Build Status Updates Python 3 GitHub codecov

Table of content

Requirements

The package requires Python 3.4+ and Django 1.8+. It has been tested on Linux, Windows and Mac OS X.

Installation

To install the package, just run:

$ pip install django-cloudinary-storage

If you need to upload any video files, run:

$ pip install django-cloudinary-storage[video]

which will additionally install python-magic for uploaded video validation.

Also, in case you use Django ImageField, make sure you have Pillow installed:

$ pip install Pillow

Once you have done that, add cloudinary and cloudinary_storage to you installed apps in your settings.py. If you are going to use this package for static and/or media files, make sure that cloudinary_storage is before django.contrib.staticfiles:

INSTALLED_APPS = [
    # ...
    'cloudinary_storage',
    'django.contrib.staticfiles',
    'cloudinary',
    # ...
]

because django-cloudinary-storage overwrites Django collectstatic command. If you are going to use it only for media files though, it is django.contrib.staticfiles which has to be first:

INSTALLED_APPS = [
    # ...
    'django.contrib.staticfiles',
    'cloudinary_storage',
    'cloudinary',
    # ...
]

Next, you need to add Cloudinary credentials to settings.py:

CLOUDINARY_STORAGE = {
    'CLOUD_NAME': 'your_cloud_name',
    'API_KEY': 'your_api_key',
    'API_SECRET': 'your_api_secret'
}

Instead of putting credentials in settings.py, you can provide them as CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_SECRET and CLOUDINARY_API_KEY environment variables. It is possible as well to set only CLOUDINARY_URL variable, combining all the information, for example:

$ export CLOUDINARY_URL=cloudinary://your_api_key:your_api_secret@your_cloud_name

For those of you who use Heroku, that's a very good news, because you won't need to set it yourself, as Heroku sets CLOUDINARY_URL environment variable for you (provided you use Cloudinary as Heroku addon).

Also, be aware that settings.py takes precedence over environment variables.

Usage with media files

The package provides three media storages:

  • cloudinary_storage.storage.MediaCloudinaryStorage for images
  • cloudinary_storage.storage.RawMediaCloudinaryStorage for raw files, like txt, pdf
  • cloudinary_storage.storage.VideoMediaCloudinaryStorage for videos

Above distinction if necessary as Cloudinary API needs to know resource type in many of its methods.

Now, let's consider the most probable scenario that you will use Cloudinary for images uploaded by users of your website. Let's say you created a following Django model:

class TestModel(models.Model):
    name = models.CharField(max_length=100)
    image = models.ImageField(upload_to='images/', blank=True)

All you need to do is to add two lines to settings.py:

MEDIA_URL = '/media/'  # or any prefix you choose
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'

And that's it! All your models with ImageField will be connected to Cloudinary.

Now, in order to put this image into your template, you can just type:

<img src="{{ test_model_instance.image.url }}" alt="{{ test_model_instance.image.name }}">

However, doing that in this way, the image will be downloaded with its original size, as uploaded by a user. To have more control, you can use Cloudinary image transformations. For example, to change the image's size, use below code:

{% load cloudinary %}
{% cloudinary test_model_instance.image.name width=100 height=100 %}

Of course, this only scratched the surface. Cloudinary is extremely powerful and I highly recommend you to check pycloudinary documentation.

Now, if you only need to use Cloudinary for images, you can skip the rest of this subsection. However, if you are going to use it for videos and/or raw files, let's continue.

Usage with raw files

If your users can upload text or other raw files, but not images, you would just use different default storage in settings.py:

DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.RawMediaCloudinaryStorage'

But what if they could upload both types? Well, not a problem! Just set DEFAULT_FILE_STORAGE setting to the most common resource type, and for fields of different type, you will need to set a correct storage individually, like this:

from django.db import models

from cloudinary_storage.storage import RawMediaCloudinaryStorage

class TestModelWithRawFileAndImage(models.Model):
    name = models.CharField(max_length=100)
    raw_file = models.ImageField(upload_to='raw/', blank=True, storage=RawMediaCloudinaryStorage())
    image = models.ImageField(upload_to='images/', blank=True)  # no need to set storage, field will use the default one

In above example we assumed DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage', that's why we set storage explicitly only for raw_file.

Usage with video files

Usage with video files is analogous to raw files, but you will need to use validate_video validator for video fields to validate user's uploaded videos. If not, Cloudinary will raise an error if a user tries to upload non-video file, which will crash your website. Of course, you could use your own validator, but if you want to use built-in one, do it like this:

from django.db import models

from cloudinary_storage.storage import VideoMediaCloudinaryStorage
from cloudinary_storage.validators import validate_video

class TestModelWithVideoAndImage(models.Model):
    name = models.CharField(max_length=100)
    video = models.ImageField(upload_to='videos/', blank=True, storage=VideoMediaCloudinaryStorage(),
                              validators=[validate_video])
    image = models.ImageField(upload_to='images/', blank=True)  # no need to set storage, field will use the default one

Usage with static files

In order to move your static files to Cloudinary, update your settings.py:

STATIC_URL = '/static/'
STATICFILES_STORAGE = 'cloudinary_storage.storage.StaticHashedCloudinaryStorage'

After that, run Django collectstatic command:

$ python manage.py collectstatic

Please note that only files with hashed name will be uploaded by default - this behavior can be changed by adding --upload-unhashed-files argument to collectstatic command. If you are not sure why it is useful to add hash to file names, shortly speaking, it allows static files to be safely cached by Cloudinary CDN and web browsers. Without it files' modification would become very problematic, because your website's users would use their private older copies. Hashing prevents this issue as any file change will change its url as well, which would force a browser to download a new version of a file.

Also, be aware that collectstatic will create a JSON file, which shows mapping of unhashed file names to their hashed versions. This file will be available at ./manifest/staticfiles.json by default - you could change that in your settings.py, for example:

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

CLOUDINARY_STORAGE = {
    # other settings, like credentials
    'STATICFILES_MANIFEST_ROOT': os.path.join(BASE_DIR, 'my-manifest-directory')
}

It is highly recommended to keep up-to-date version of this file in your version control system.

In order to use static files from Cloudinary, make sure you write your templates in below style:

{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'images/dummy-static-image.jpg' %}" alt="dummy static image">

In Django 1.10 and later, you could use {% load static %} instead of {% load static from staticfiles %}.

If you would like to apply Cloudinary transformations for static images or videos, please use cloudinary_static template tag as follows:

{% load cloudinary_static %}
{% cloudinary_static 'images/dummy-static-image.jpg' width=50 height=50 %}

You can adjust STATIC_IMAGES_EXTENSIONS and STATIC_VIDEOS_EXTENSIONS to set rules which file extensions are treated as image or video files. Files with different extensions will be uploaded as Cloudinary raw files and no transformations could be applied for those files. Also, please note that cloudinary_static is just a thin wrapper around cloudinary tag from pycloudinary library, so please go to its documentation to see what transformations are possible.

Please note that you must set DEBUG to False to fetch static files from Cloudinary. With DEBUG equal to True, Django staticfiles app will use your local files for easier and faster development (unless you use cloudinary_static template tag).

Management commands

The package provides three management commands:

  • collectstatic
  • deleteorphanedmedia
  • deleteredundantstatic

collectstatic

Adds minor modifications to Django collectstatic to improve upload performance. It uploads only hashed files as the default. Also, it uploads a file only when necessary, namely it won't upload the file if a file with the same name and content will be already uploaded to Cloudinary, which will save both time and bandwidth.

Optional arguments:

  • --upload-unhashed-files - uploads files without hash added to their name along with hashed ones, use it only when it is really necessary
  • --noinput - non-interactive mode, the command won't ask you to do any confirmations

deleteorphanedmedia

Deletes needless media files, which are not connected to any model. It is possible to provide paths to prevent deletion of given files in EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS in settings.py, for example:

CLOUDINARY_STORAGE = {
    # other settings
    'EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS': ('path/', 'second-path/')
}

Optional arguments:

  • --noinput - non-interactive mode, the command won't ask you to do any confirmations

deleteredundantstatic

Deletes needless static files.

Optional arguments:

  • --keep-unhashed-files - use it if you use collectstatic with --upload-unhashed-files argument, without it this command will always delete all unhashed files
  • --noinput - non-interactive mode, the command won't ask you to do any confirmations

Settings

Below you can see all available settings with default values:

import os

from django.conf import settings

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

CLOUDINARY_STORAGE = {
    'CLOUD_NAME': None,  # required
    'API_KEY': None,  # required
    'API_SECRET': None,  # required
    'SECURE': True,
    'MEDIA_TAG': 'media',
    'INVALID_VIDEO_ERROR_MESSAGE': 'Please upload a valid video file.',
    'EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS': (),
    'STATIC_TAG': 'static',
    'STATICFILES_MANIFEST_ROOT': os.path.join(BASE_DIR, 'manifest'),
    'STATIC_IMAGES_EXTENSIONS': ['jpg', 'jpe', 'jpeg', 'jpc', 'jp2', 'j2k', 'wdp', 'jxr',
                                 'hdp', 'png', 'gif', 'webp', 'bmp', 'tif', 'tiff', 'ico'],
    'STATIC_VIDEOS_EXTENSIONS': ['mp4', 'webm', 'flv', 'mov', 'ogv' ,'3gp' ,'3g2' ,'wmv' ,
                                 'mpeg' ,'flv' ,'mkv' ,'avi'],
    'MAGIC_FILE_PATH': 'magic',
    'PREFIX': settings.MEDIA_URL
}

CLOUD_NAME, API_KEY and API_SECRET are mandatory and you need to define them in CLOUDINARY_STORAGE dictionary in settings.py, the rest could be overwritten if required, as described below:

  • SECURE - whether your Cloudinary files should be server over HTTP or HTTPS, HTTPS is the default, set it to False to switch to HTTP
  • MEDIA_TAG - name assigned to your all media files, it has to be different than STATIC_TAG, usually you don't need to worry about this setting, it is useful when you have several websites which use the same Cloudinary account, when you should set it unique to distinguish it from other websites,
  • INVALID_VIDEO_ERROR_MESSAGE - error message which will be displayed in user's form when one tries to upload non-video file in video field
  • EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS - looked by deleteorphanedmedia command, you can provide here tuple of paths which will never be deleted
  • STATIC_TAG - name assigned to your all static files, it has to be different than MEDIA_TAG, please see MEDIA_TAG setting to see when it is useful
  • STATICFILES_MANIFEST_ROOT - path where staticfiles.json will be saved after collectstatic command, ./manifest is the default location
  • STATIC_IMAGES_EXTENSIONS - list of file extensions with which static files will be treated as Cloudinary images
  • STATIC_VIDEOS_EXTENSIONS - list of file extensions with which static files will be uploaded as Cloudinary videos
  • MAGIC_FILE_PATH: applicable only for Windows, needed for python-magic library for movie validation, please see python-magic for reference
  • PREFIX - prefix to your all files uploaded by MediaCloudinaryStorage, default MEDIA_URL, it can be useful when you use FileSystemStorage as default and MediaCloudinaryStorage for some models fields

How to run tests

First, install tox:

$ pip install tox

After that, edit tox.ini file and input your Cloudinary credentials in setenv.

Then, just run:

$ tox

which will execute tests for Python 2.7, 3.4 - 3,5 and Django 1.8 - 1.11 (and additionally for Python 3.6 and Django 1.11). At the end you will see coverage report in your console. HTML version of this report will be available in ./htmlcov/index.html file.

If you only need to run tests for your environment, add -e argument to tox command in {py34,py35}-dj{18,19,110,111} format (plus py36-dj111), for example:

$ tox -e py34-dj110

which will run tests for Python 3.4 and Django 1.10.

django-cloudinary-storage's People

Contributors

klis87 avatar seroy avatar tiagocordeiro avatar timgates42 avatar tundebabzy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-cloudinary-storage's Issues

Unsupported video format or file

Hello i am trying to upload a video to cloudinary but i keep getting this error
Traceback (most recent call last): File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner response = get_response(request) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 614, in wrapper return self.admin_site.admin_view(view)(*args, **kwargs) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view response = view_func(request, *args, **kwargs) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func response = view_func(request, *args, **kwargs) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 233, in inner return view(request, *args, **kwargs) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1656, in change_view return self.changeform_view(request, object_id, form_url, extra_context) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper return bound_method(*args, **kwargs) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view response = view_func(request, *args, **kwargs) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1534, in changeform_view return self._changeform_view(request, object_id, form_url, extra_context) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1580, in _changeform_view self.save_model(request, new_object, form, not add) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1093, in save_model obj.save() File "/home/otchere-dev/Desktop/Programming/Django/incompleted projects/udemy_clone/courses/models.py", line 113, in save return super().save(*args, **kwargs) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/db/models/base.py", line 753, in save self.save_base(using=using, force_insert=force_insert, File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/db/models/base.py", line 790, in save_base updated = self._save_table( File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/db/models/base.py", line 869, in _save_table values = [(f, None, (getattr(self, f.attname) if raw else f.pre_save(self, False))) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/db/models/base.py", line 869, in <listcomp> values = [(f, None, (getattr(self, f.attname) if raw else f.pre_save(self, False))) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/db/models/fields/files.py", line 307, in pre_save file.save(file.name, file.file, save=False) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/db/models/fields/files.py", line 87, in save self.name = self.storage.save(name, content, max_length=self.field.max_length) File "/home/otchere-dev/.local/lib/python3.8/site-packages/django/core/files/storage.py", line 52, in save return self._save(name, content) File "/home/otchere-dev/.local/lib/python3.8/site-packages/cloudinary_storage/storage.py", line 68, in _save response = self._upload(name, content) File "/home/otchere-dev/.local/lib/python3.8/site-packages/cloudinary_storage/storage.py", line 62, in _upload return cloudinary.uploader.upload(content, **options) File "/home/otchere-dev/.local/lib/python3.8/site-packages/cloudinary/uploader.py", line 46, in upload return call_cacheable_api("upload", params, file=file, **options) File "/home/otchere-dev/.local/lib/python3.8/site-packages/cloudinary/uploader.py", line 394, in call_cacheable_api result = call_api(action, params, http_headers, return_error, unsigned, file, timeout, **options) File "/home/otchere-dev/.local/lib/python3.8/site-packages/cloudinary/uploader.py", line 477, in call_api raise Error(result["error"]["message"]) cloudinary.exceptions.Error: Unsupported video format or file

Below is the model
class Episode(models.Model): title=models.CharField(max_length=225) file=models.FileField(upload_to='courses',validators=[validate_video],storage=VideoMediaCloudinaryStorage()) length=models.DecimalField(max_digits=100,decimal_places=2)

Screenshot from 2021-07-11 19-36-20

When i try uploading the same video as a rawfile it works but i can play it in the browser and also image uploading aslo works perfectly.

Please any help would be really be appreciated. Thank you

Splitting static between Cloudinary and production server

Hi there!

Do you think it would be hard to split static between Cloudinary and the "usual" manifest storage?Here is a use case I have: while Cloudinary's CDN is the best place for most of static stuff, a few critical resources (e.g. CSS) are pushed (with http2 push) on page load. Of course you can't push stuff from a different domain, so right now it's either CDN or http2 push. It would be nice to be able to skip CDN for some files.

One (simple) way of doing that is to override url() in my project. However, I'm not sure if it will not break something. What do you think?

This backend doesn't support absolute paths

I was trying to upload image to cloudinary by decreasing the quality of image. I run into this error while uploading image.

Code

Models.py

class MyModel(models.Model):
      image = models.ImageField(upload_to='image/')
      .....

     def save(self, *args, **kwargs):
          instance = super(MyModel, self).save(*args, **kwargs)
          img = Image.open(self.image.path)
          img.save(self.image.path, quality=10, optimize=True)
          return instance
Logs
Traceback (most recent call last):
  File "venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/venv/lib/python3.9/site-packages/django/contrib/admin/options.py", line 614, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/venv/lib/python3.9/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "venv/lib/python3.9/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/venv/lib/python3.9/site-packages/django/contrib/admin/sites.py", line 233, in inner
    return view(request, *args, **kwargs)
  File "/lib/python3.9/site-packages/django/contrib/admin/options.py", line 1653, in add_view
    return self.changeform_view(request, None, form_url, extra_context)

    return self._changeform_view(request, object_id, form_url, extra_context)
  File "/venv/lib/python3.9/site-packages/django/contrib/admin/options.py", line 1580, in _changeform_view
    self.save_model(request, new_object, form, not add)
  File "venv/lib/python3.9/site-packages/django/contrib/admin/options.py", line 1093, in save_model
    obj.save()
  File "/mymodels/models.py", line 33, in save
    img = Image.open(self.image.path)
  File "venv/lib/python3.9/site-packages/django/db/models/fields/files.py", line 57, in path
    return self.storage.path(self.name)
  File "/lib/python3.9/site-packages/django/core/files/storage.py", line 116, in path
    raise NotImplementedError("This backend doesn't support absolute paths.")

Exception Type: NotImplementedError at /admin/mymodels/add/
Exception Value: This backend doesn't support absolute paths.

This backend doesn't support absolute paths.

I am trying to upload an image following the documentation but getting the error : This backend doesn't support absolute paths.
here is my code.
models.py
`from django.db import models

from django.contrib.auth.models import User
from multiselectfield import MultiSelectField

Create your models here.

class profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
CLASS_CHOICES = [
('Class - IX', 'Class - IX'),
('Class X', 'Class X'),
]
standard = models.CharField(choices=CLASS_CHOICES, default='Class X', max_length=10)
roll_number = models.SmallIntegerField(blank=True,primary_key=True)
# bio = models.TextField(max_length=500, blank=True)
# location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
image = models.ImageField(upload_to='profile_pics/', default='img_avatar.png')

class Meta:
    permissions = [
        ('is_student', 'User is student')
    ]
        # ('is_teacher', 'User is teacher'),
def __str__(self):
    return f'{self.roll_number}. {self.user.first_name}  {self.user.last_name}' 

`
views.py
def handle_save(request):
if request.method == 'POST':
profile_pic = request.FILES['new-profile-img']
print(profile_pic)

    try:
        instance = profile.objects.get(user=request.user)
    except:
        instance = teacher.objects.get(user= request.user)

    finally:    
        print(instance.image.path)
        instance.image = profile_pic
        instance.save()
        return redirect('profile')

Cannot find valid magic files.

When uploading videos I get the cannot find valid magic files error if i use Filefield and if I use imagefield i get upload a valid image error

How can I delete an image each time I delete the instance asociated with it?

If I have a model that has an ImageField as an attribute, how can I delete the image each time I do model_instance.delete()? I've seen that there is the deleteorphanedmedia command, but I don't understand how to use it each time I delete something that has an image.
What extra steps do I need to add to achieve this so I stop having orphan images?

All static gets uploaded as "raw"

The title says it all.

One of the reasons why that's less than ideal is limits, e.g. Cloudinary's size limit for "raw" files is lower than for videos.

If I understand correctly, it's not an intended behavior.

I'm using the "default" setup (as shown on README.md), here is a piece of debug log during collectstatic:

Copying '/blahblah/build/media/instructions.jpg'
DEBUG 2017-06-16 17:19:08,706 connectionpool.py:_new_conn:818 Starting new HTTPS connection (1): res.cloudinary.com
DEBUG 2017-06-16 17:19:08,857 connectionpool.py:_make_request:395 https://res.cloudinary.com:443 "HEAD /blahblah/raw/upload/v1/static/media/instructions.jpg HTTP/1.1" 404 0
/Users/si14/.virtualenvs/melsciwebpy3/lib/python3.5/site-packages/urllib3/connectionpool.py:852: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
DEBUG 2017-06-16 17:19:09,193 connectionpool.py:_make_request:395 https://api.cloudinary.com:443 "POST /v1_1/blahblah/raw/upload HTTP/1.1" 200 504

As you can see, stuff gets uploaded to /raw/.

How to get the full path

This package is awesome, but I have one question, when I query something that has an ImageField, how do I get the full path? Now I get only something like media/images/uploaded_image.png

NOTE: I'm using graphql to query this, so I don't know if there if the rest of the path is completed by the cloudinary tag in the template or something like that

Setting for prefix

Currently prefix is based on MEDIA_URL setting. It would be nice to have abbility to customize prefix via CLOUDINARY_STORAGE. I can make PR for this, if it's necessary.

Static files don't work

Hi there, running collectstatic raises the following errors.

hashed

Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 187, in handle
    collected = self.collect()
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 128, in collect
    for original_path, processed_path, processed in processor:
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary_storage/storage.py", line 313, in post_process
    for response in super(HashCloudinaryMixin, self).post_process(paths, dry_run, **options):
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py", line 399, in post_process
    yield from super().post_process(*args, **kwargs)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py", line 231, in post_process
    for name, hashed_name, processed, _ in self._post_process(paths, adjustable_paths, hashed_files):
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py", line 288, in _post_process
    content = pattern.sub(converter, content)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py", line 187, in converter
    hashed_url = self._url(
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py", line 126, in _url
    hashed_name = hashed_name_func(*args)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py", line 338, in _stored_name
    cache_name = self.clean_name(self.hashed_name(name))
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary_storage/storage.py", line 284, in hashed_name
    content = open(absolute_path, 'rb')
TypeError: expected str, bytes or os.PathLike object, not NoneType

unhashed

Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 187, in handle
    collected = self.collect()
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 114, in collect
    handler(path, prefixed_path, storage)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary_storage/management/commands/collectstatic.py", line 30, in copy_file
    super(Command, self).copy_file(path, prefixed_path, source_storage)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 348, in copy_file
    self.storage.save(prefixed_path, source_file)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/django/core/files/storage.py", line 54, in save
    return self._save(name, content)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary_storage/storage.py", line 236, in _save
    super(StaticCloudinaryStorage, self)._save(name, content)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary_storage/storage.py", line 68, in _save
    response = self._upload(name, content)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary_storage/storage.py", line 195, in _upload
    return cloudinary.uploader.upload(content, public_id=name, resource_type=resource_type,
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary/uploader.py", line 46, in upload
    return call_cacheable_api("upload", params, file=file, **options)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary/uploader.py", line 394, in call_cacheable_api
    result = call_api(action, params, http_headers, return_error, unsigned, file, timeout, **options)
  File "/home/ubuntu/repos/therepublic-app/venv/lib/python3.8/site-packages/cloudinary/uploader.py", line 477, in call_api
    raise Error(result["error"]["message"])
cloudinary.exceptions.Error: Empty file

Error during management command: deleteredundantstatic

Currently getting this error when running deleteredundantstatic. It is happening in get_needful_files, and the tuple is coming from here in Django code I believe. Presumably, it should just be using the first paths value from the tuple? Not sure if I am missing configuration or anything but that is certainly possible as well.

AttributeError: 'tuple' object has no attribute 'values'

Unable to load large videos

I tried to upload a video of about 20MB. It just keeps loading. Nothing happens!

The browser just keeps loading.

Add ability to manipualate option upload in settings.py

An example of this is when you want to avoid adding a suffix for every filename you to add unique_filename: False in the dictionary value of options variable in _upload. I did this manually changing the inside of the library.

Can I set uppload option to private?

Hi, first thanks for the amazing project!

I want to upload my files in private mode, so they are only accessible by my application, is it possible with this backend?

I have checked the documentation and I haven't found anything regarding private mode.

Thanks!

Static pictures transformation

Hi there!

Quick question: how do you apply Cloudinary's transformation to static media? Did I miss something in the README, or is there no way to transform static pictures at the moment?

issue with exists for raw files

For some reason https://github.com/klis87/django-cloudinary-storage/blob/master/cloudinary_storage/storage.py#L83 doesnt work anymore for raw files, at least according to tests. For example below url for some reason gets 404 https://res.cloudinary.com/dri8awewt/raw/upload/v1/static/bd5f98a5-4524-408f-882f-fe31786c9ab7 during tests, but when I check cloudinary, this file exists, but it has a different version, but in the past v1 worked anyway...

@tiagocordeiro @bufke any ideas?

django-cloudinary-storage does not anticipate Windows paths

If a document is named using Windows styled path, the _save method causes problems.

def _save(self, name, content):
    # name is path\to\some\image\image.jpg on Windows machine
    name = self._prepend_prefix(name)
    # name becomes media/path\to\some\image\image.jpg if `settings.MEDIA_URL` is /media/
    ...

At the end, the resulting filename on cloudinary is something like media/\path\to\some\image\image_asdfghjk.jpg/ causing cloudinary to complain.

I think it will be nice if django-cloudinary-storage also works well on Windows. I can supply a fix which makes sure the name variable in _save is in unix style.

How to apply cloudinary transformations outside of templates

I am storing a profile picture for each user in our database.

class User(AbstractBaseUser, PermissionsMixin):
    profile_picture = models.ImageField(
        upload_to="profile_picture/",
        blank=True,
        null=True,
    )

Now, I would like to return a cropped or otherwise transformed version of the profile_picture to a client requesting it via a REST API. What method can I use to get a CloudinaryImage instance of the image?

File extension '' not allowed

I'm having some issues using ImageField in models. After uploading an image via an admin form and then going back to do some edits on that newly created entity, I get File extension '' is not allowed... when hitting Save.

Is there a good way around this? Right now I feel like anything I try to do to avoid this error is hackish.

Thanks for the lib by the way, really helping me out!

Document client-side uploading

Hello!

I am currently using django-cloudinary-storage with the naive, server-uploading method, where the user uploads the photo to the server and the server uploads it to Cloudinary. That works fine, but soon I will switch to client-uploaded images, where the user uploads the image directly to Cloudinary and then sends me the URL (or whatever Cloudinary returns).

Could some documentation be added to detail how I can support this method? What do I need to give Django to create these fields and how?

File size too large -- but it isn't

I'm trying to upload using the admin, and am getting

cloudinary.api.Error: File size too large. Got 28078738. Maximum is 10485760.

However, the file itself is only about 2MB. It has worked for the longest time and only recently have I seen this error. Thoughts?

Implement upload options

Hey thanks for creating what I believe is the correct way to do this!

I'd like to request the ability to specify upload options: My specific use-case is incoming transformations.

I'm not entirely sure how this would be communicated to the storage layer but declaring the options on the model's field would be nice.

With pycloudinary the way they've supported this is to allow you to extend and override CloudinaryField.upload_options(). I don't like this style though as it's preferred to specify options like this as arguments to the field's constructor.

Issue with uploading Django CKEditor images to cloudinary

Everything is working fine if I don't use Cloudinary. But when I am using Cloudinary for storing media files, it is throwing error:

Internal Server Error: /ckeditor/upload/
Traceback (most recent call last):
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\django\contrib\auth\decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\django\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\django\views\generic\base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\ckeditor_uploader\views.py", line 118, in post
    saved_path = filewrapper.save_as(filepath)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\ckeditor_uploader\backends\pillow_backend.py", line 54, in save_as
    saved_path = self.storage_engine.save(filepath, self.file_object)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\django\core\files\storage.py", line 52, in save
    return self._save(name, content)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\cloudinary_storage\storage.py", line 68, in _save
    response = self._upload(name, content)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\cloudinary_storage\storage.py", line 62, in _upload
    return cloudinary.uploader.upload(content, **options)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\cloudinary\uploader.py", line 46, in upload
    return call_cacheable_api("upload", params, file=file, **options)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\cloudinary\uploader.py", line 341, in call_cacheable_api
    result = call_api(action, params, http_headers, return_error, unsigned, file, timeout, **options)
  File "C:\Users\ashut\.virtualenvs\Blog-OHIX9BKG\lib\site-packages\cloudinary\uploader.py", line 428, in call_api
    raise Error(result["error"]["message"])
cloudinary.exceptions.Error: Invalid image file

My urls patterns :

urlpatterns = [
    path('admin/', admin.site.urls),
    path('ckeditor/', include('ckeditor_uploader.urls')),
    path('accounts/',include('authentication.urls')),
    path('',include('core.urls')),
]

if settings.DEBUG:
	urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
	urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

My media files storage settings :

CLOUDINARY_STORAGE = {
    'CLOUD_NAME': config("CLOUD_NAME"),
    'API_KEY': config("API_KEY"),
    'API_SECRET': config("API_SECRET")
}


# Media Files
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'


# CKEditor Configurations
CKEDITOR_UPLOAD_PATH = "blog/uploads/"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.