An Extense Guide On Handling Images In Django

As a web developer, working with images is an important skill to have. Django is one of the most popular web development frameworks, it provides ways to deal with images conveniently.

In this tutorial, we are going to build a Django application that handles images. By building this, you shall learn how to set up your Django project environment to handle images and also how to perform other operations on the images.

Table of contents

Pre-requisites

To follow through this tutorial, you need to have:

  • Fundamental knowledge of both Python and Django.
  • A code editor like Visual Studio Code.

Set up the environment

We shall start by setting up the development environment for our Django project.

In your command line, create a new directory for our project and name it Images as shown:

mkdir Images

Then, enter the directory, create a virtual environment and activate it respectively using the commands below:

cd Images

py -m venv .venv

.venv

\S

cripts

\a

ctivate.bat

Now, we will need to install the following libraries:

  • Django helps build our website using the Django web development framework.
  • Pillow is an image library that we shall use to manipulate our images.

pip install django

pip install pillow

After the installation, we now proceed to create the Django project named myGallery:

django

-

admin startproject myGallery

cd myGallery

py manage

.

py startapp demo

Now, we need to register our demo app in the list of installed apps in the settings.py file as shown:

INSTALLED_APPS

=

[

'django.contrib.admin'

,

'django.contrib.auth'

,

'django.contrib.contenttypes'

,

'django.contrib.sessions'

,

'django.contrib.messages'

,

'django.contrib.staticfiles'

,

'demo'

,

# new

]

We need to tell Django which URL to use for serving the media files and also specify the root directory where our images will be stored.

We shall do this using MEDIA_URL and MEDIA_ROOT respectively. In the settings.py file, we will add the following:

import

os

MEDIA_URL

=

'/media/'

MEDIA_ROOT

=

os

.

path

.

join(BASE_DIR,

'media'

)

To enable a user to upload images during development, we need to add the following settings in our project-level urls.py for testing purposes. It should look like this:

from

django.contrib

import

admin

from

django.urls

import

path

from

django.conf

import

settings

from

django.conf.urls.static

import

static

urlpatterns

=

[

path(

'admin/'

, admin

.

site

.

urls),

]

+

static(settings

.

MEDIA_URL, document_root

=

settings

.

MEDIA_ROOT)

# New

The above snippet tells Django where to find user-uploaded images when the project is still in the development stage.

Create and register the models

We can now proceed and define a model that shall be used to store the images in our app.

In Django, a default database is automatically created for you. All you have to do is add the tables called models.

In models.py, we shall create a model with two fields, the title of a photo and the photo as shown:

from

django.db

import

models

class

Image

(models

.

Model):

title

=

models

.

CharField(max_length

=

20

)

photo

=

models

.

ImageField(upload_to

=

'pics'

)

The upload_to tells Django to store the photo in a directory called pics under the media directory.

Now, we can register our model in admin.py as shown:

from

django.contrib

import

admin

from

.models

import

Image

class

imageAdmin

(admin

.

ModelAdmin):

list_display

=

[

"title"

,

"photo"

]

admin

.

site

.

register(Image, imageAdmin)

The list_display list tells Django admin to display its contents in the admin dashboard. The contents are the model’s fields.

In this case, we want it to display the title and photo fields of every image that is uploaded.

Display the images

So far, we made it possible to upload the images using the Django admin, but we also need to display the images on our site.

So, let’s add the display template, display view, and also configure the URLs.

Display view

Views in Django are used to send requests to and from the server. The requests can be to return a page, query the database, make calculations, and so on.

In views.py, we add:

from

django.shortcuts

import

render

from

.models

import

Image

# Create your views here.

def

index

(request):

data

=

Image

.

objects

.

all()

context

=

{

'data'

: data

}

return

render(request,

"display.html"

, context)
  • The above function-based view will return all the Image objects from our database when requested.

Display template

In our app demo, we will create a directory named templates and create a file named display.html inside it.

Inside display.html we will add the following code to help display all the uploaded images:

<!doctype html>

<

html

lang

=

"en"

>

<

head

>

<!-- Required meta tags -->

<

meta

charset

=

"utf-8"

>

<

meta

name

=

"viewport"

content

=

"width=device-width, initial-scale=1"

>

<!-- Bootstrap CSS -->

<

link

href

=

"https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"

rel

=

"stylesheet"

integrity

=

"sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"

crossorigin

=

"anonymous"

>

<

title

>Demo Gallery</

title

>

</

head

>

<

body

class

=

"container "

style

=

"padding-top: 5%;"

>

<

div

class

=

"row"

>

{% for x in data %}

<

div

class

=

"col-md-4"

>

<

div

class

=

"thumbnail"

>

<

a

href

=

"{{ x.photo.url }}"

>

<

img

src

=

"{{ x.photo.url }}"

alt

=

"Lights"

style

=

"width:100%"

>

<

div

class

=

"caption"

>

<

p

>{{ x.title }}</

p

>

</

div

>

</

a

>

</

div

>

</

div

>

{% endfor %}

</

div

>

</

body

>

</

html

>

Configure the URLs

At the project level (myGallery), we will add some code to point the root URL to our demo app’s URLs:

from

django.contrib

import

admin

from

django.urls

import

path, include

from

django.conf

import

settings

from

django.conf.urls.static

import

static

urlpatterns

=

[

path(

'admin/'

, admin

.

site

.

urls),

path(

''

, include(

'demo.urls'

))

# new

]

+

static(settings

.

MEDIA_URL, document_root

=

settings

.

MEDIA_ROOT)

Now, we shall create another file named urls.py in our demo app directory. This new urls.py file is supposed to contain the urlpatterns of our app.

Add the following code to it:

from

django.urls

import

path

from

.import views

urlpatterns

=

[

path(

''

, views

.

index, name

=

'index'

),

]

  • The root path ('') in the pattern above points towards the index view, which in return gets the images and renders them together with the display.html.

Testing

To test if the upload and display work, we can go ahead and run the server. But, before all that, we will first have to make the migrations in our database, then create the superuser.

In our cmd, we shall go ahead and run:

py manage.py makemigrations

py manage.py migrate

  • makemigrations – generates the SQL commands for the new Images model.
  • migrate – executes the SQL commands generated by the makemigrations command.

We can now create our admin account using the command below:

py manage.py createsuperuser

  • createsuperuser – Django comes with a ready-built admin site that can be used to administrate the users and the database of the site.

When you run this command, you will be prompted to enter credentials to login into your account.

Run the following command to start the localhost server:

py manage.py runserver

In the Admin site, add some images to check how they appear on the display page.

On adding four images from my PC, here is what my web page looks like:

display.html

Now that we have looked at the basic concepts to deal with images, we shall proceed to the advanced concepts next.

User image upload

In this case, we are going to look at how to enable a user to upload an image using forms.

In our app-level directory demo, create a file and call it forms.py. In the file, we shall create the class to handle the upload form and link it to our Image model as shown:

from

django

import

forms

from

.models

import

Image

class

ImageUploadForm

(forms

.

ModelForm):

class

Meta

:

model

=

Image

fields

=

[

'title'

,

'photo'

]

With the above fields, Django will present us with the input fields in our form.

Now, let’s create an HTML form to display those input fields. In our templates folder, add a file named upload.html and add this code to it:

<!DOCTYPE html>

<

html

lang

=

"en"

>

<

head

>

<

meta

charset

=

"UTF-8"

>

<

meta

http-equiv

=

"X-UA-Compatible"

content

=

"IE=edge"

>

<

meta

name

=

"viewport"

content

=

"width=device-width, initial-scale=1.0"

>

<

title

>Image Upload</

title

>

</

head

>

<

body

>

<

form

method

=

"POST"

enctype

=

"multipart/form-data"

>

{% csrf_token %}

{{ form.as_p }}

<

button

type

=

"submit"

> Upload Image </

button

>

</

form

>

</

body

>

</

html

>

In the above HTML form:

  • method="POST" requests the server to accept the submission of a form.
  • enctype="multipart/form-data" encodes files in a certain way that allows them to be submitted through POST method.
  • {% csrf_token %} enables us to protect out site from Cross Site Request Forgeries.
  • {{ form.as_p }} displays the form fields wrapped around with the paragraph’s HTML tags

Next, we will create a function-based view that handles the image upload. In views.py, update your code as shown:

from

django.shortcuts

import

render, redirect

# new

from

.models

import

Image

from

.forms

import

ImageUploadForm

# new

def

index

(request):

data

=

Image

.

objects

.

all()

context

=

{

'data'

: data

}

return

render(request,

"display.html"

, context)

# new

def

uploadView

(request):

if

request

.

method

==

'POST'

:

form

=

ImageUploadForm(request

.

POST, request

.

FILES)

if

form

.

is_valid():

form

.

save()

return

redirect(

'index'

)

else

:

form

=

ImageUploadForm()

return

render(request,

'upload.html'

, {

'form'

: form})

Upon the submission of the form, it will be checked if the request is a POST request. If so, the ImageUploadForm will be called to handle it.

We then go ahead to check the validity of the form before saving it and redirecting the users to the display page.

The only remaining part is updating our urls.py. In our apps level urls.py file, update it as shown:

from

django.urls

import

path

from

.import views

urlpatterns

=

[

path(

''

, views

.

index, name

=

'index'

),

path(

'upload_image/'

, views

.

uploadView, name

=

'upload_image'

)

# new

]

  • The new path upload_image points to the uploadView which takes care of the upload process.

Here is what mine looks like :

upload.html

List the images in the Admin site

As of now, our admin site images are listed using their names and paths:

before

This makes it hard to identify the images without having to click on the links. We can easily improve this by listing the images on the dashboard, alongside their names.

We will edit our Image model in models.py, to add an image_tag field that helps render the images as shown:

from

django.db

import

models

from

django.utils.safestring

import

mark_safe

# new

# Create your models here.

class

Image

(models

.

Model):

title

=

models

.

CharField(max_length

=

20

)

photo

=

models

.

ImageField(upload_to

=

'pics'

)

def

image_tag

(self):

# new

return

mark_safe(

'<img src="/../../media/

%s

" width="150" height="150" />'

%

(self

.

photo))
  • The mark_safe tells the Django templates to render the string as such (render it as text using &lt;&gt;). Therefore, it renders the HTML image tag with its path and size for every image.

Now, we need to update admin.py to include the image_tag in the list_display:

from

django.contrib

import

admin

from

.models

import

Image

class

imageAdmin

(admin

.

ModelAdmin):

list_display

=

[

"title"

,

"image_tag"

,

"photo"

]

# new

admin

.

site

.

register(Image, imageAdmin)

As a result, our Image dashboard should resemble this:

after

Create thumbnails

If you click on images in the display page, they tend to have different default sizes. The big images might even take longer to load due to their size.

In case you want to change this behavior and give them a reasonable size, you would have to override the save method in our Image model.

We will create thumbnails for the images by editing their size during the uploading process, and set the maximum value of width and height to 300.

Let’s update our models.py as shown:

from

django.db

import

models

from

django.utils.safestring

import

mark_safe

from

PIL

import

Image

as

Im

# new

# Create your models here.

class

Image

(models

.

Model):

title

=

models

.

CharField(max_length

=

20

)

photo

=

models

.

ImageField(upload_to

=

'pics'

)

def

image_tag

(self):

return

mark_safe(

'<img src="/../../media/

%s

" width="150" height="150" />'

%

(self

.

photo))

def

save

(self):

# new

super()

.

save()

img

=

Im

.

open(self

.

photo

.

path)

# resize it

if

img

.

height

>

300

or

img

.

width

>

300

:

output_size

=

(

300

,

300

)

img

.

thumbnail(output_size)

img

.

save(self

.

photo

.

path)

Add a background image in a Django template

We shall go on and add a background image to our upload template. To do so, we shall create a directory named static in our app-level directory demo, and add the background image that you intend to use inside it.

The background image that we use is bj.jpg.

In upload.html, we shall load the statics at the top, then add the background image in the body tag. Update your upload.html as shown:

{% load static %}

<!DOCTYPE html>

<

html

lang

=

"en"

>

<

head

>

<

meta

charset

=

"UTF-8"

>

<

meta

http-equiv

=

"X-UA-Compatible"

content

=

"IE=edge"

>

<

meta

name

=

"viewport"

content

=

"width=device-width, initial-scale=1.0"

>

<

title

>Image Upload</

title

>

</

head

>

<

body

style

=

"background-image: url('{% static 'bg.jpg' %}');"

>

<

form

method

=

"POST"

enctype

=

"multipart/form-data"

>

{% csrf_token %}

{{ form.as_p }}

<

button

type

=

"submit"

> Upload Image </

button

>

</

form

>

</

body

>

</

html

>

After that, we shall change the STATIC_URL in settings.py from the first code block below to the second code block.

STATIC_URL

=

'static/'

STATIC_URL

=

'/static/'

This enables Django to find the static files within the static folder.

Conclusion

To conclude, we looked at a live code implementation on how to handle images in Django. We built a Django project to enable users to upload images and view them. Also, we enabled the admins to view the thumbnail version of the images.

I hope you have learned how to implement these solutions into your Django projects.

You can find the full source code here.

Happy coding!

Peer Review Contributions by: Srishilesh P S