Simple Django Tip #9

Simple Django Tip #9

How to provide a default image for your Profile picture?

Image attribute: Social media illustrations by Storyset

Introduction

I started to write about Django tips in concurrence to my development with a product I'm building. As and when I complete developing a logical step, I take the crux of it and share it in a post. As I wanted to share my progress in public, I thought this would be an aspect to it πŸ’β€β™€οΈ.

In my previous post on Django tips, we looked at how to conditionally navigate post successful login. In this post, let's look at different ways to provide a default image to your profile picture aka avatar. I chose this one as it's common practice for every single User model to have a placeholder for a profile picture.

πŸ’‘
I've noticed that I grasp a concept easily when explained with a business case or a real life example rather than a foo bar type of an explanation. Hence I try to apply the same whenever I explain a concept. Hence the need for 'avatar' πŸ•΅οΈβ€β™€οΈ in this post. But it holds good for any scenario where there is a need for a default image 🎨

Different Approaches

For the sake of the post, let's define a CustomUser model like so πŸ‘‡

class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    display_name = models.CharField(max_length=20, \
                            null=True, blank=True)
    first_name = models.CharField(max_length=150, \
                            validators=[validators.validate_aplhabets_only],\
                                null=True, blank=True)
    last_name = models.CharField(max_length=150, 
                            validators=[validators.validate_aplhabets_only], \
                                 null=True, blank=True)
    avatar = models.ImageField(<< will be expplained below >>)
    contact_number = models.CharField(max_length=25, null=True, blank=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)
    # denotes whether the user clicked on activation/confirmation link
    is_account_verified = models.BooleanField(default=False)
    # denotes the date when the user joined the platform
    date_added = models.DateTimeField(auto_now_add=True)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

    objects = CustomUserManager()


    def __str__(self):
        return f"{self.email}"

The column avatar is the one that is of interest. There are multiple ways to handle it and let's look at a couple of them.

  1. Default Image while defining the Model

    We can provide a default path to the user's avatar. Just make sure to load the image in the mentioned folder. Usually it will resemble one of the images shown in this link.

     avatar = models.ImageField(upload_to="avatars/", null=True, blank=True, default='path/to/default/avatar.jpg')
    

    This is one of the most straight forward method to handle a default image.

  2. Default Image while accessing the image in the Template

    If we do not want a default image in the Model as modifying something in the model will lead to migrations and it's better not to keep changing details in a Django Model. An alternate approach will be to handle it directly in the template itself.

    If the user's avatar column has a value, then that is used, else the default image.

    πŸ’‘
    One needs to upload the default image in this case too
     {% if user.avatar %}
         <img src="{{ user.avatar.url }}" alt="User avatar">
     {% else %}
         <img src="{% static 'path/to/default/avatar.jpg' %}" alt="Default avatar">
     {% endif %}
    

    Note the usage of {% static %} template tag in case of default image.

    πŸ’‘ Do not forget to {% load static %}

  3. Using a Helper function in the Model definition class

    A third approach is to use a helper function in the Model class of CustomUser. This function will return the avatar url if it has been set else the path to the default. ⭐In my opinion, this is a much cleaner approach as all things CustomUser is within the Model itself without altering any aspect at a column definition.

     class CustomUser(AbstractBaseUser, PermissionsMixin):
         # ...
    
         def get_avatar_url(self):
             if self.avatar and hasattr(self.avatar, 'url'):
                 return self.avatar.url
             else:
                 return 'path/to/default/avatar.jpg'
    

    We then access the function directly in the template like so πŸ‘‡

     <img src="{{ user.get_avatar_url }}" alt="User avatar">
    
  4. Using a templatetag

    In case your project has a lot of image fields with a need for default, then consider using templatetags.

    πŸ’‘
    If you are interested to learn more about templatetags, here is a good one from Django documentation. I plan to write a separate post about templatetags in near future 😁

Create a folder in your django project and name it templatetags. Create all your custom tags under this folder. For our avatar, let's create a file and name it image_tags.py.

As a first step, create an instance for template.Library(). Write a simple function that returns the default image if the user's avatar is not set, else return the set avatar url.

πŸ’‘
Don't forget to add the decorator!
from django import template

register = template.Library()

@register.simple_tag
def get_avatar_url(user):
    return user.avatar.url if user.avatar else 'path/to/default/avatar.jpg'

In your template, you can make use of the newly created custom tag like so πŸ‘‡

{% load image_tags %}

<img src="{% get_avatar_url user %}" alt="User avatar">

Conclusion

This is a short post that talks about four different approaches when you need to show an image by default for an ImageField in Django. Factors to keep in mind while choosing in approach can be:

  • Is it an one off case?

  • Are there a lot of image fields?

  • Not many developers are going to touch the code base

  • Size of the Django project

    Hope you liked this post and don't forget to share the approach that you usually take 😁

Β