Integrate Cloudinary with Django Models

In this post, we will discuss how we can integrate Cloudinary with Django models and forms. We will build a blog project with a posts app, and each post will have a title field, a content field, and an image field. We will discuss how we can upload that image to the Cloudinary cloud.

Install the Cloudinary module
 

First, let's install the Cloudinary module. From your terminal type:

pip3 install cloudinary

Start a New Django Project
 

Let's start a new Django project. In the terminal type:

django-admin startproject my_blog

Then change directories into this folder:

cd my_blog

Create a Posts App
 

Next, we will create a posts app:

python manage.py startapp posts

Let's activate this app. Let's open the settings.py file and add this app to the INSTALLED_APPS list as follows:

INSTALLED_APPS = [
    'posts.apps.PostsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Set Up Cloudinary

Also, we'll add the Cloudinary module to this list:

INSTALLED_APPS = [
    'posts.apps.PostsConfig',
    'cloudinary',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Next, we import Cloudinary in settings.py and provide our Cloudinary credentials that we can get from the Cloudinary dashboard. You must provide the cloud_name, the api_key, and the api_secret:

import cloudinary
cloudinary.config(
cloud_name = 'xxxxxxxxx',
api_key = 'xxxxxxxxxxxxxxxx',
api_secret = 'xxx-xxxxxxxxxxxxxxxxxxxxxxx',
)

Add a Post Model
 

First, we'll import the CloudinaryField class. Let's open the posts/models.py file and add this:

from cloudinary.models import CloudinaryField

Next, we define the Post class:

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    image = CloudinaryField("image")

The Post model has a title field, a content field, and also an image field of the CloudinaryField class.

Let's generate the migration file:

python manage.py makemigrations

Next, we run this migration:

python manage.py migrate

Define a PostForm class


Inside the posts/ folder, let's add a forms.py file. We import the ModelForm class, our Post model, and also the CloudinaryFileField class:

from django.forms import ModelForm
from .models import Post
from cloudinary.forms import CloudinaryFileField

Here, we define a PostForm class as follows:

class PostForm(ModelForm):
    class Meta:
        model = Post
        fields = ["title", "content", "image"]
    image = CloudinaryFileField(
        options={"folder": "blog/", "crop": "limit", "width": 600, "height": 600,}
    )

This class has a form field image of the CloudinaryFileField class. You can specify some options. Here, I've chosen a folder inside my Cloudinary cloud where I want my images to go, as well as the image sizes.

Write the views

Let's now write the views. First, import these in the posts/views.py file:

from django.shortcuts import get_object_or_404, render
from .models import Post
from .forms import PostForm
from django.http import HttpResponseRedirect
from django.urls import reverse

Next. let's add a new view, which we'll generate the form to add a new post:

def new(request):
    if request.method == "POST":
        form = PostForm(request.POST, request.FILES)
        if form.is_valid():
            post = form.save(commit=True)
            return HttpResponseRedirect(reverse("posts:detail", args=[post.id]))
        else:
            form = PostForm()
    return render(request, "posts/new.html", {"form": PostForm()})

If the form is valid, we'll redirect the user to the post page with its details. 

We need to create this detail view:

def detail(request, id):
    post = get_object_or_404(Post, id=id)
    return render(request, "posts/detail.html", {"post": post})

Finally, we will have an index view:

def index(request):
    posts = Post.objects.all()
    return render(request, "posts/index.html", {"posts": posts})

Define Our URLs


We do not have URLs for these views, so let's define them. Let's create a urls.py file in the posts/ folder and edit it as follows:

from django.urls import path
from . import views
app_name = "posts"
urlpatterns = [
    path("", views.index, name="index"),
    path("new", views.new, name="new"),
    path("<int:id>/", views.detail, name="detail"),
]

Let's add the path to the posts application in the  my_blog/urls.py file:

First, import the include function, and then add the path:
 

from django.contrib import admin
from django.urls import include, path
urlpatterns = [
   path('admin/', admin.site.urls),
   path('posts/', include('posts.urls'))
]

Write the Templates
 

The last thing we have to do is to write our templates. Let's create a templates/ folder inside the posts/ app. Inside the templates/ folder, we add a posts/ sub-folder where we'll put the three templates. 

Let's first create the new.html template with the form:

<!DOCTYPE html>
<html>
  <head>
    <title>New Post</title>
  </head>
  <body>
    <h1>New Post</h1>
    <form action="{% url 'posts:new' %}" method='post' enctype='multipart/form-data'>
      {% csrf_token %}
      {{ form.as_p }}
      <input type='Submit'>
    </form>
  </body>
</html>

Next, we'll add the detail.html template where we'll first initialize Cloudinary tags:

{% load cloudinary %}

Then we can embed the image using the cloudinary template tag. This is the detail.html template:

<!DOCTYPE html>
<html>
  <head>
    <title>{{ post.title }}</title>
  </head>
  <body>
    <h1>{{ post.title }}</h1>
    {% cloudinary post.image width=600 height=600 crop='fill' alt=post.title %}
    <p>{{ post.content }}</p>
  </body>
</html>

 We need our index.html template, too, so let's write it:

<!DOCTYPE html>
<html>
  <head>
    <title>Posts</title>
  </head>
  <body>
    <h1>Posts</h1>
    <ul>
      {% for post in posts %}
        <li><a href = "{% url 'posts:detail' post.id %}">{{ post.title }}</a></li>
      {% endfor %}
    </ul>
    <p><a href="{% url 'posts:new' %}">New Post</a></p>
  </body>
</html>

Now, it's time to start the server and add some posts. Run:

python manage.py runserver

Then we can go to http://localhost:8000/posts/new to add new posts with images uploaded to the Cloudinary cloud.

Post last updated on May 13, 2023