You have probably noticed by now that lots of websites with a social element will have persistent avatars for users even if they've never uploaded one themselves. Probably the most common solution is just to rely on a third party service like Gravatar, and that's a reasonable choice. If you want to do that, implementation in Django is dead simple. That said if you don't want to rely on a third party service, or if you want avatars specific to your site (rather than the "Globally Recognized" avatars Gravatar promises), it turns out that implementing this yourself in Django is actually really easy.
The basic problem is a simple one - we want to generate a randomized image for a user, and we want that image to be persistent. The naive approach is to generate an image once and store it on the server. This works, but at the cost of storing redundant data; we can do better. Since each user already has a unique, persistant id we can use that to generate a distinct image procedurally without storing any files or data. Better yet, by using a custom Django template tag we can implement this with a minimum of code. Here's a simple implementation:
<pre># projects/templatetags/avatar.py
from django import template
from django.template.loader import render_to_string
import hashlib
register = template.Library()
NUM_COLORS = 10
@register.filter
def avatar(user):
letter = user.name[0].upper() if user.name else '?'
color_ix = int(hashlib.md5(str(user.id).encode()).hexdigest(), 16) % NUM_COLORS
context = {'color_ix': color_ix, 'letter': letter}
return render_to_string('accounts/avatar.svg', context)
# accounts/templates/accounts/avatar.svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" class="avatar color-{{ color_ix }}">
<rect x="0" y="0" width="100%" height="100%"></rect>
<text x="50%" y="50%">{{ letter }}</text>
</svg>
</pre>
We just {% load avatar %}
in our template and use the tag with {{
user|avatar }}
where user
is a user object. For instance, if we want to
show the logged in user's avatar somewhere we'd use {{ request.user|avatar
}}
(assuming we have context_processors.request
installed). Since we're
just rendering inline SVGs, we can use CSS to manage fonts, colors, and
layout. Another advantage of the SVGs is that there are no external
dependencies.
Fancied up with a little CSS (thanks to Justin!) here's an example of the output:
For the sake of clarity, I've ignored unicode issues and stuck to just 10
distinct images per letter. The md5
hash is just there to shuffle things up
so that the colors aren't an obvious function of user id. This example bases
the color on the user id and includes the first letter of the user's name in
the avatar. This has the advantage that the color will always be the same for a
given user, though the letter could change if a user changes his name. An
avatar based only on the id would be immutable, which might be better for
some purposes. There's also an argument for basing the avatar on the email
address like gravatar does. There are a lot of options, and they're all pretty
easy to implement! For instance, I wanted users to be able to upload their own
avatars, so I check for a custom avatar in the filter before falling back on
the generated one.
This particular avatar is designed to be as simple as possible, but the
possibilities are endless. All we need to do is to write Python code that
generates images based on an integer. A general approach would be to use the
random
module to generate an image and then we can use random.seed
with the
hash to make the output consistent per user. Generating a unique image like
this for each user wouldn't be
hard!
So with about 20 lines of code (and a little CSS) we've implemented basic, persistent, custom avatars. It definitely makes me happy to be using Django!
Fusionbox is a custom Django development agency based in Denver, Colorado. All of our Python/Django engineers are senior level and work out of our office in Denver. No outsourcing here! We specialize in both creating new Django software and taking over projects that may be stalled or that need more resources. If you need help with your, get in touch!