When we try to create profiles for our users, we need to upload the images. There are also many other scenarios where we need to add image to our data model. We need to upload the image to our upload folders and then we have to serve those images on the website whenever the views are rendered. Django is batteries included framework which means you don’t have to handle lot of stuff by yourself, and Django provides you the method to handle the complex tasks which seems very hard if you have to do from scratch.
In today’s post we are going to talk about how to add the images to your Models in the Django and how to serve them in your templates which means to display the images. It also means to directly access those images via some URL. If we talk about the official documentation which talks about serving the images and adding to the models here is the link to the official documentation which seems pretty confusing at first. Because it does not provide any beneficial example. So, I decided to create this blog post to talk about some practical use case and minimum code required to get jobs done.
Add Image Field to the Models
First of all let’s talk about how to define a Django Model which accepts the Image. We can create a Custom User object which can accept the Images as profile pictures and then provide the URL whenever required like the following code example.
class CustomUser(AbstractUser):
is_voter = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_candidate = models.BooleanField(default=False)
profile_picture = models.ImageField(upload_to='profile_pictures',blank=True,null=True)
address = models.CharField(max_length=255,blank=True,null=True)
phone_number = models.CharField(max_length=15,blank=True,null=True)
date_of_birth = models.DateField(blank=True,null=True)
gender = models.CharField(max_length=10,blank=True,null=True)
cnic_number = models.CharField(max_length=15,blank=True,null=True)
def __str__(self):
return self.username
Code language: Python (python)
Now, If you do not handle the settings to define the media folder, the profile_picture
field is going to create a new folder under the root folder which the name profile_pictures
if not already exists and save all the profile pictures in that folder. It will also replace any existing image with the same name. So you have to talke the cases where the image name could be same and image is already exist in the profile_picture folder. You can either embed the user id along with the profile picture or you can simply create a unique UUID for each image. Either why, that is not the scope of the today’s topic. Let’s just focus on handling the profile pictures.
Define a Custom Media Folder
We can change the settings to define the custom Media folder. Let’s suppose we want to use the media
folder to be used for all kind of model medias. We can define that in our project’s settings.py
file. Just open the settings file and add the following MEDIA_ROOT
and MEDIA_URL
settings.
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Code language: Python (python)
You may also need to create the respective folder with mkdir media
command under the root directory of your project.
Handle Profile Update in Django
Now to handle the profile picture upload and update option we can create a new Form inside our forms.py
file. You may need to create this file if not already exists. You can create this file under your authentication app or any other respective app which is handling the users. Inside this forms.py
file create a very simple form to update the profile for the sake of simplicity. You can modify this form if you which later to handle custom formatting’s and label options according to your requirements. But for now here is the simplest form which handle the profile picture option.
class CustomUserUpdateForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ['first_name', 'last_name', 'address', 'profile_picture','phone_number', 'date_of_birth', 'gender', 'cnic_number']
Code language: Python (python)
Now create a view to update the profile. Here is how you can create a simple view which handle the profile update feature in Django.
@login_required
def update_profile(request):
user = request.user
if request.method == 'POST':
form = CustomUserUpdateForm(request.POST, request.FILES, instance=user)
if form.is_valid():
form.save()
return redirect('authentication:profile_updated')
else:
messages.error(request, 'Please correct the error below.')
# return render(request, 'settings/update_profile.html', {'form': form,'form_errors': form_errors})
else:
form = CustomUserUpdateForm(instance=user)
# Pass form errors to the template
form_errors = form.errors.as_data()
return render(request, 'settings/update_profile.html', {'form': form,'form_errors': form_errors,'crisp_form':CrispyAddressForm})
@login_required
def profile_updated(request):
return render(request, 'settings/profile_updated.html')
Code language: Python (python)
Here is the templates which simply serve the forms and display the update success messages.
{% extends 'layouts/voter_base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block title %}
Update Profile
{% endblock title %}
<!-- include css from static voter_assets/dashbard.css -->
{% block page_title %}
<h1>Update Profile</h1>
{% endblock page_title %}
{% block content %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6 shadow p-3 mb-5 bg-white rounded">
<form method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
<table>{{ form.as_table }}</table>
<div class="mt-3">
<button type="submit" class="btn btn-primary btn-lg">Update Profile</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
Code language: HTML, XML (xml)
and for the profile updated template you can simply display the message like this.
{% extends 'layouts/voter_base.html' %}
{% load static %}
{% block title %}
Profile Updated
{% endblock title %}
{% block content %}
<div class="container mt-5">
<h2>Profile Updated</h2>
<div class="alert alert-success" role="alert">
Your profile has been updated.
</div>
<a href="{% url 'dashboard' %}" class="btn btn-primary">Go Back</a>
</div>
{% endblock content %}
Code language: HTML, XML (xml)
Here is the profile Updated message.