How to Crop images using Django Site

Cropping images is common use case in a Web application. For example, some applications let you upload a picture for profile. In addition, it usually also let you crop image for a better result. But, when dealing with image processing we need to install a few dependencies, both in the front-end and in the back-end Using JavaScript with bootstrap.

In this Blog I will explain how to Upload an image and Crop the image and finally upload it and save in the server/Database

First of all we have to install some more dependencies as we are dealing with image uploading and croping

Installing Extra Dependencies

Within your project directory or virtualenv, install Pillow :

pip install pillow

Background

Create project and app using below commands

django-admin startproject crop

cd crop

python manage.py startapp crop_image

django-admin startproject crop
cd crop
python manage.py startapp crop_image

Consider the following Model

models.py

from django.db import models

class Image(models.Model):
    file = models.ImageField(upload_to='images')
    uploaded = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return str(self.pk)

Create a urls.py Under crop_image then Consider below Code

urls.py

from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from crop_image.views import main_view

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', main_view, name="main-view")
]

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

Consider below file as forms

forms.py

from django import forms
from .models import Image

class ImageForm(forms.ModelForm):
    class Meta:
        model = Image
        fields = ('file',)

On rendering above froms we wiil get as below

Consider below views file:

views.py

from django.shortcuts import render
from .forms import ImageForm
from django.http import JsonResponse

def main_view(request):
    form = ImageForm(request.POST or None, request.FILES or None)
    if form.is_valid():
        form.save()
        return JsonResponse({'message': 'works'})
    context = {'form': form}
    return render(request, 'templates/main.html', context)

Make Some changes in settings.py file

Add our app in the installed apps block as below

INSTALLED_APPS = [
  ...
    'crop_image',
  ...
]

And then add templates and static in settings.py file

TEMPLATES = 
[
    {
 

        ...

        'DIRS': [BASE_DIR,'templates'],
        ....
       
    },
]

.
.
.

STATIC_URL = 'static/'
STATICFILES_DIRS=[BASE_DIR/'static']

MEDIA_URL ='/media/'
MEDIA_ROOT =BASE_DIR /'media'

Using Bootstrap 3 in this, so my base template looks like below;

base.html

{% load static %}
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- favicon -->
    <link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}"/>
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">

    <!-- jquery -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>

    <!-- cropper -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/cropper/4.1.0/cropper.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropper/4.1.0/cropper.min.css">

    <!-- custom css & js-->
    <link rel="stylesheet" href={% static 'style.css' %}>
    <script src={% static 'main.js' %} defer></script>

    <title>image cropper</title>
  </head>
  <body>
    <div class="container mt-3">
        {% block content %}
        {% endblock content %}
    </div>
  </body>
</html>

main.html

{% extends 'base.html' %}

{% block content %}
<div id="alert-box"></div>
<div id="image-box" class="mb-3"></div>
<form action="" id="image-form">
    {% csrf_token %}
    {{form.as_p}}
</form>
<button class="btn btn-primary mt-3 not-visible" id="confirm-btn">confirm</button>
{% endblock content %}

Image Crop Template

In order to crop the image, we need four pieces of information: X coordinate, Y coordinate, height and width of the cropping box the user will eventually use with in the browser.

We are adding croppinng techniques in main.js Javascript file as below

main.js

console.log('hello world')
const alertBox = document.getElementById('alert-box')
const imageBox = document.getElementById('image-box')
const imageForm = document.getElementById('image-form')
const confirmBtn = document.getElementById('confirm-btn')
const input = document.getElementById('id_file')

const csrf = document.getElementsByName('csrfmiddlewaretoken')

input.addEventListener('change', ()=>{
    alertBox.innerHTML = ""
    confirmBtn.classList.remove('not-visible')
    const img_data = input.files[0]
    const url = URL.createObjectURL(img_data)

    imageBox.innerHTML = `<img src="${url}" id="image" width="700px">`
    var $image = $('#image')
    console.log($image)

    $image.cropper({
        aspectRatio: 16 / 9,
        crop: function(event) {
            console.log(event.detail.x);
            console.log(event.detail.y);
            console.log(event.detail.width);
            console.log(event.detail.height);
            console.log(event.detail.rotate);
            console.log(event.detail.scaleX);
            console.log(event.detail.scaleY);
        }
    });
    
    var cropper = $image.data('cropper');
    confirmBtn.addEventListener('click', ()=>{
        cropper.getCroppedCanvas().toBlob((blob) => {
            console.log('confirmed')
            const fd = new FormData();
            fd.append('csrfmiddlewaretoken', csrf[0].value)
            fd.append('file', blob, 'my-image.png');

            $.ajax({
                type:'POST',
                url: imageForm.action,
                enctype: 'multipart/form-data',
                data: fd,
                success: function(response){
                    console.log('success', response)
                    alertBox.innerHTML = `<div class="alert alert-success" role="alert">
                                            Successfully saved and cropped the selected image
                                        </div>`
                },
                error: function(error){
                    console.log('error', error)
                    alertBox.innerHTML = `<div class="alert alert-danger" role="alert">
                                            Ups...something went wrong
                                        </div>`
                },
                cache: false,
                contentType: false,
                processData: false,
            })
        })
    })
})

Example css file below

style.css

.not-visible {
    display: none;
}

Make Sure the Files are under the order

└── crop
  ├── init .py
  ├── pycache
  ├── asgi.py
  ├── settings.py
  ├── urls.py
  └── wsgi.p
└── crop_image
  ├── admin.py
  ├── models.py
  ├── forms.py
  ├── urls.py
  ├── views.py
└── templates
  ├── main.html
  ├── base.html
└── static
  ├── main.js
  ├── style.css
├── db.sqlite3
├── manage.py
  

On basis of above configaration the Following output will generate

. Uploading image and Cropping the Image
Cropped image

Volia !.. We are successfully Completed Image Cropping in Django Site….

ThankYou…

GitHub link :> https://github.com/saikumar248/Image_Cropping.git