{"id":17481323,"url":"https://github.com/klis87/django-cloudinary-storage","last_synced_at":"2025-04-05T05:08:34.164Z","repository":{"id":38184903,"uuid":"63619003","full_name":"klis87/django-cloudinary-storage","owner":"klis87","description":"Django package that provides Cloudinary storages for both media and static files as well as management commands for removing unnecessary files.","archived":false,"fork":false,"pushed_at":"2024-06-18T18:51:20.000Z","size":1129,"stargazers_count":131,"open_issues_count":14,"forks_count":27,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-29T04:11:39.885Z","etag":null,"topics":["cloudinary","django","python","static-files"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/klis87.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-07-18T16:25:36.000Z","updated_at":"2025-03-05T12:35:03.000Z","dependencies_parsed_at":"2024-06-18T19:57:40.981Z","dependency_job_id":"5026a3b8-f56b-4f92-8e75-bcf64631b779","html_url":"https://github.com/klis87/django-cloudinary-storage","commit_stats":{"total_commits":116,"total_committers":5,"mean_commits":23.2,"dds":"0.15517241379310343","last_synced_commit":"6f9ad3f61656df3c22ae1815d35c84c7fd4d264a"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klis87%2Fdjango-cloudinary-storage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klis87%2Fdjango-cloudinary-storage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klis87%2Fdjango-cloudinary-storage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klis87%2Fdjango-cloudinary-storage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/klis87","download_url":"https://codeload.github.com/klis87/django-cloudinary-storage/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289428,"owners_count":20914464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudinary","django","python","static-files"],"created_at":"2024-10-18T22:09:26.141Z","updated_at":"2025-04-05T05:08:34.146Z","avatar_url":"https://github.com/klis87.png","language":"Python","funding_links":[],"categories":["Storage","Python"],"sub_categories":[],"readme":"# Django Cloudinary Storage\n\nDjango Cloudinary Storage is a Django package that facilitates integration with [Cloudinary](http://cloudinary.com/)\nby implementing [Django Storage API](https://docs.djangoproject.com/en/1.11/howto/custom-file-storage/).\nWith several lines of configuration, you can start using Cloudinary for both media and static files.\nAlso, it provides management commands for removing unnecessary files, so any cleanup will be a breeze.\nIt uses [pycloudinary](https://github.com/cloudinary/pycloudinary) package under the hood.\n\n[![Build Status](https://travis-ci.org/tiagocordeiro/django-cloudinary-storage.svg?branch=master)](https://travis-ci.org/tiagocordeiro/django-cloudinary-storage)\n[![Updates](https://pyup.io/repos/github/tiagocordeiro/django-cloudinary-storage/shield.svg)](https://pyup.io/repos/github/tiagocordeiro/django-cloudinary-storage/)\n[![Python 3](https://pyup.io/repos/github/tiagocordeiro/django-cloudinary-storage/python-3-shield.svg)](https://pyup.io/repos/github/tiagocordeiro/django-cloudinary-storage/)\n[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/tiagocordeiro/django-cloudinary-storage/blob/master/LICENSE)\n[![codecov](https://codecov.io/gh/tiagocordeiro/django-cloudinary-storage/branch/master/graph/badge.svg)](https://codecov.io/gh/tiagocordeiro/django-cloudinary-storage)\n\n## Table of content\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Usage with media files](#usage-with-media-files)\n  - [Usage with raw files](#usage-with-raw-files)\n  - [Usage with video files](#usage-with-video-files)\n- [Usage with static files](#usage-with-static-files)\n- [Management commands](#management-commands)\n  - [collectstatic](#collectstatic)\n  - [deleteorphanedmedia](#deleteorphanedmedia)\n  - [deleteredundantstatic](#deleteredundantstatic)\n- [Settings](#settings)\n- [How to run tests](#how-to-run-tests)\n\n## Requirements\n\nThe package requires Python 3.4+ and Django 1.8+. It has been tested on Linux, Windows and Mac OS X.\n\n## Installation\n\nTo install the package, just run:\n\n```\n$ pip install django-cloudinary-storage\n```\n\nIf you need to upload any video files, run:\n\n```\n$ pip install django-cloudinary-storage[video]\n```\n\nwhich will additionally install [python-magic](https://github.com/ahupp/python-magic) for uploaded video validation.\n\nAlso, in case you use Django `ImageField`, make sure you have Pillow installed:\n\n```\n$ pip install Pillow\n```\n\nOnce you have done that, add `cloudinary` and `cloudinary_storage` to you installed apps in your `settings.py`. If you\nare going to use this package for static and/or media files, make sure that `cloudinary_storage` is before `django.contrib.staticfiles`:\n\n```python\nINSTALLED_APPS = [\n    # ...\n    'cloudinary_storage',\n    'django.contrib.staticfiles',\n    'cloudinary',\n    # ...\n]\n```\n\nbecause django-cloudinary-storage overwrites Django `collectstatic` command. If you are going to use it only for media files\nthough, it is `django.contrib.staticfiles` which has to be first:\n\n```python\nINSTALLED_APPS = [\n    # ...\n    'django.contrib.staticfiles',\n    'cloudinary_storage',\n    'cloudinary',\n    # ...\n]\n```\n\nNext, you need to add Cloudinary credentials to `settings.py`:\n\n```python\nCLOUDINARY_STORAGE = {\n    'CLOUD_NAME': 'your_cloud_name',\n    'API_KEY': 'your_api_key',\n    'API_SECRET': 'your_api_secret'\n}\n```\n\nInstead of putting credentials in `settings.py`, you can provide them as `CLOUDINARY_CLOUD_NAME`,\n`CLOUDINARY_API_SECRET` and `CLOUDINARY_API_KEY` environment variables. It is possible as well to set only\n`CLOUDINARY_URL` variable, combining all the information, for example:\n\n```\n$ export CLOUDINARY_URL=cloudinary://your_api_key:your_api_secret@your_cloud_name\n```\n\nFor those of you who use Heroku, that's a very good news, because you won't need to set it yourself, as Heroku sets\n`CLOUDINARY_URL` environment variable for you (provided you use Cloudinary as Heroku addon).\n\nAlso, be aware that `settings.py` takes precedence over environment variables.\n\n## Usage with media files\n\nThe package provides three media storages:\n\n- `cloudinary_storage.storage.MediaCloudinaryStorage` for images\n- `cloudinary_storage.storage.RawMediaCloudinaryStorage` for raw files, like txt, pdf\n- `cloudinary_storage.storage.VideoMediaCloudinaryStorage` for videos\n\nAbove distinction if necessary as Cloudinary API needs to know resource type in many of its methods.\n\nNow, let's consider the most probable scenario that you will use Cloudinary for images uploaded by users of your\nwebsite. Let's say you created a following Django model:\n\n```python\nclass TestModel(models.Model):\n    name = models.CharField(max_length=100)\n    image = models.ImageField(upload_to='images/', blank=True)\n```\n\nAll you need to do is to add two lines to `settings.py`:\n\n```python\nMEDIA_URL = '/media/'  # or any prefix you choose\nDEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'\n```\n\nAnd that's it! All your models with `ImageField` will be connected to Cloudinary.\n\nNow, in order to put this image into your template, you can just type:\n\n```django\n\u003cimg src=\"{{ test_model_instance.image.url }}\" alt=\"{{ test_model_instance.image.name }}\"\u003e\n```\n\nHowever, doing that in this way, the image will be downloaded with its original size, as uploaded by a user.\nTo have more control, you can use Cloudinary image transformations. For example, to change the image's size,\nuse below code:\n\n```django\n{% load cloudinary %}\n{% cloudinary test_model_instance.image.name width=100 height=100 %}\n```\n\nOf course, this only scratched the surface. Cloudinary is extremely powerful and I highly recommend you to check\n[pycloudinary](https://github.com/cloudinary/pycloudinary) documentation.\n\nNow, if you only need to use Cloudinary for images, you can skip the rest of this subsection.\nHowever, if you are going to use it for videos and/or raw files, let's continue.\n\n### Usage with raw files\n\nIf your users can upload text or other raw files, but not images, you would just use different default storage\nin `settings.py`:\n\n```python\nDEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.RawMediaCloudinaryStorage'\n```\n\nBut what if they could upload both types? Well, not a problem! Just set `DEFAULT_FILE_STORAGE` setting to the most\ncommon resource type, and for fields of different type, you will need to set a correct storage individually, like this:\n\n```python\nfrom django.db import models\n\nfrom cloudinary_storage.storage import RawMediaCloudinaryStorage\n\nclass TestModelWithRawFileAndImage(models.Model):\n    name = models.CharField(max_length=100)\n    raw_file = models.ImageField(upload_to='raw/', blank=True, storage=RawMediaCloudinaryStorage())\n    image = models.ImageField(upload_to='images/', blank=True)  # no need to set storage, field will use the default one\n```\n\nIn above example we assumed `DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'`,\nthat's why we set storage explicitly only for `raw_file`.\n\n### Usage with video files\n\nUsage with video files is analogous to raw files, but you will need to use `validate_video` validator for video fields\nto validate user's uploaded videos. If not, Cloudinary will raise an error if a user tries to upload non-video file,\nwhich will crash your website. Of course, you could use your own validator, but if you want to use built-in one,\ndo it like this:\n\n```python\nfrom django.db import models\n\nfrom cloudinary_storage.storage import VideoMediaCloudinaryStorage\nfrom cloudinary_storage.validators import validate_video\n\nclass TestModelWithVideoAndImage(models.Model):\n    name = models.CharField(max_length=100)\n    video = models.ImageField(upload_to='videos/', blank=True, storage=VideoMediaCloudinaryStorage(),\n                              validators=[validate_video])\n    image = models.ImageField(upload_to='images/', blank=True)  # no need to set storage, field will use the default one\n```\n\n## Usage with static files\n\nIn order to move your static files to Cloudinary, update your `settings.py`:\n\n```python\nSTATIC_URL = '/static/'\nSTATICFILES_STORAGE = 'cloudinary_storage.storage.StaticHashedCloudinaryStorage'\n```\n\nAfter that, run Django `collectstatic` command:\n\n```\n$ python manage.py collectstatic\n```\n\nPlease note that only files with hashed name will be uploaded by default - this behavior can be changed by adding\n`--upload-unhashed-files` argument to `collectstatic` command. If you are not sure why it is useful to add hash to file\nnames, shortly speaking, it allows static files to be safely cached by Cloudinary CDN and web browsers. Without it\nfiles' modification would become very problematic, because your website's users would use their private older copies.\nHashing prevents this issue as any file change will change its url as well, which would force a browser to download\na new version of a file.\n\nAlso, be aware that `collectstatic` will create a JSON file, which shows mapping of unhashed file names to their hashed\nversions. This file will be available at `./manifest/staticfiles.json` by default - you could change that\nin your `settings.py`, for example:\n\n```python\nimport os\n\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n\nCLOUDINARY_STORAGE = {\n    # other settings, like credentials\n    'STATICFILES_MANIFEST_ROOT': os.path.join(BASE_DIR, 'my-manifest-directory')\n}\n```\n\nIt is highly recommended to keep up-to-date version of this file in your version control system.\n\nIn order to use static files from Cloudinary, make sure you write your templates in below style:\n\n```django\n{% load static %}\n\u003clink rel=\"stylesheet\" href=\"{% static 'css/style.css' %}\"\u003e\n\u003cimg src=\"{% static 'images/dummy-static-image.jpg' %}\" alt=\"dummy static image\"\u003e\n```\n\nIn Django 1.10 and later, you could use `{% load static %}` instead of `{% load static from staticfiles %}`.\n\nIf you would like to apply Cloudinary transformations for static images or videos, please use `cloudinary_static`\ntemplate tag as follows:\n\n```django\n{% load cloudinary_static %}\n{% cloudinary_static 'images/dummy-static-image.jpg' width=50 height=50 %}\n```\n\nYou can adjust `STATIC_IMAGES_EXTENSIONS` and `STATIC_VIDEOS_EXTENSIONS` to set rules which file extensions are treated\nas image or video files. Files with different extensions will be uploaded as Cloudinary raw files and no transformations\ncould be applied for those files. Also, please note that `cloudinary_static` is just a thin wrapper around `cloudinary`\ntag from [pycloudinary](https://github.com/cloudinary/pycloudinary) library, so please go to its documentation\nto see what transformations are possible.\n\nPlease note that you must set `DEBUG` to `False` to fetch static files from Cloudinary. With `DEBUG` equal to `True`,\nDjango `staticfiles` app will use your local files for easier and faster development (unless you use\n`cloudinary_static` template tag).\n\n## Management commands\n\nThe package provides three management commands:\n\n- `collectstatic`\n- `deleteorphanedmedia`\n- `deleteredundantstatic`\n\n### collectstatic\n\nAdds minor modifications to Django `collectstatic` to improve upload performance. It uploads only hashed files\nas the default. Also, it uploads a file only when necessary, namely it won't upload the file if a file with the same\nname and content will be already uploaded to Cloudinary, which will save both time and bandwidth.\n\nOptional arguments:\n\n- `--upload-unhashed-files` - uploads files without hash added to their name along with hashed ones, use it only\n  when it is really necessary\n- `--noinput` - non-interactive mode, the command won't ask you to do any confirmations\n\n### deleteorphanedmedia\n\nDeletes needless media files, which are not connected to any model. It is possible to provide paths to prevent deletion\nof given files in `EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS` in `settings.py`, for example:\n\n```python\nCLOUDINARY_STORAGE = {\n    # other settings\n    'EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS': ('path/', 'second-path/')\n}\n```\n\nOptional arguments:\n\n- `--noinput` - non-interactive mode, the command won't ask you to do any confirmations\n\n### deleteredundantstatic\n\nDeletes needless static files.\n\nOptional arguments:\n\n- `--keep-unhashed-files` - use it if you use `collectstatic` with `--upload-unhashed-files` argument,\n  without it this command will always delete all unhashed files\n- `--noinput` - non-interactive mode, the command won't ask you to do any confirmations\n\n## Settings\n\nBelow you can see all available settings with default values:\n\n```python\nimport os\n\nfrom django.conf import settings\n\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n\nCLOUDINARY_STORAGE = {\n    'CLOUD_NAME': None,  # required\n    'API_KEY': None,  # required\n    'API_SECRET': None,  # required\n    'SECURE': True,\n    'MEDIA_TAG': 'media',\n    'INVALID_VIDEO_ERROR_MESSAGE': 'Please upload a valid video file.',\n    'EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS': (),\n    'STATIC_TAG': 'static',\n    'STATICFILES_MANIFEST_ROOT': os.path.join(BASE_DIR, 'manifest'),\n    'STATIC_IMAGES_EXTENSIONS': ['jpg', 'jpe', 'jpeg', 'jpc', 'jp2', 'j2k', 'wdp', 'jxr',\n                                 'hdp', 'png', 'gif', 'webp', 'bmp', 'tif', 'tiff', 'ico'],\n    'STATIC_VIDEOS_EXTENSIONS': ['mp4', 'webm', 'flv', 'mov', 'ogv' ,'3gp' ,'3g2' ,'wmv' ,\n                                 'mpeg' ,'flv' ,'mkv' ,'avi'],\n    'MAGIC_FILE_PATH': 'magic',\n    'PREFIX': settings.MEDIA_URL\n}\n```\n\n`CLOUD_NAME`, `API_KEY` and `API_SECRET` are mandatory and you need to define them in `CLOUDINARY_STORAGE` dictionary\nin `settings.py`, the rest could be overwritten if required, as described below:\n\n- `SECURE` - whether your Cloudinary files should be server over HTTP or HTTPS, HTTPS is the default, set it to False\n  to switch to HTTP\n- `MEDIA_TAG` - name assigned to your all media files, it has to be different than `STATIC_TAG`, usually you don't\n  need to worry about this setting, it is useful when you have several websites which use the same Cloudinary account, when\n  you should set it unique to distinguish it from other websites,\n- `INVALID_VIDEO_ERROR_MESSAGE` - error message which will be displayed in user's form when one tries to upload non-video\n  file in video field\n- `EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS` - looked by `deleteorphanedmedia` command, you can provide here tuple of paths\n  which will never be deleted\n- `STATIC_TAG` - name assigned to your all static files, it has to be different than `MEDIA_TAG`, please see `MEDIA_TAG`\n  setting to see when it is useful\n- `STATICFILES_MANIFEST_ROOT` - path where `staticfiles.json` will be saved after `collectstatic` command, `./manifest`\n  is the default location\n- `STATIC_IMAGES_EXTENSIONS` - list of file extensions with which static files will be treated as Cloudinary images\n- `STATIC_VIDEOS_EXTENSIONS` - list of file extensions with which static files will be uploaded as Cloudinary videos\n- `MAGIC_FILE_PATH`: applicable only for Windows, needed for python-magic library for movie validation, please see\n  [python-magic](https://github.com/ahupp/python-magic#dependencies) for reference\n- `PREFIX` - prefix to your all files uploaded by `MediaCloudinaryStorage`, default `MEDIA_URL`, it can be useful when\n  you use `FileSystemStorage` as default and `MediaCloudinaryStorage` for some models fields\n\n## How to run tests\n\nFirst, install tox:\n\n```\n$ pip install tox\n```\n\nAfter that, edit `tox.ini` file and input your Cloudinary credentials in `setenv`.\n\nThen, just run:\n\n```\n$ tox\n```\n\nwhich 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).\nAt the end you will see coverage report in your console. HTML version of this report will be available in `./htmlcov/index.html`\nfile.\n\nIf you only need to run tests for your environment, add `-e` argument to `tox` command in\n`{py34,py35}-dj{18,19,110,111}` format (plus `py36-dj111`), for example:\n\n```\n$ tox -e py34-dj110\n```\n\nwhich will run tests for Python 3.4 and Django 1.10.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklis87%2Fdjango-cloudinary-storage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fklis87%2Fdjango-cloudinary-storage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklis87%2Fdjango-cloudinary-storage/lists"}