Once you have a site on production, new needs start showing up. For us one of those needs was to be able to send information to our users when something was happening to the application, like some update or maintenance.

There are two simple approaches to this issue:

  • Create a notification system and send the information to every inbox
  • Create a global announcement and show it anywhere

The first one is useful when the users need to know something and we cannot allow them missing the information. They will have some kind of inbox in the application and the information will be highlighted until they read it.

The second case is somewhat simpler. We need to let our current users know about something that is happening soon or right now, and after the event is over we don’t care if they know about it or they do not. A global announcement that active users can see but that does not generate any notification is a better approach on this case.

This post is about how a simple application with a couple of files can help us handle the second case: Global Announcements.

Easy to add: Create a model and manage it from admin

We want our announcements to be easy to create and display. With this in mind we can just create a model and make use of the powerful Django admin.

The model needs to be pretty simple at its core: needs a content, a flag to know if it is visible, the severity of the message and…wait, that’s it!

We would end with something like:

from django.db import models
LEVEL_CHOICES = (
     ('warning', 'Warning'),
     ('error', 'Error'),
     ('success', 'Success'),
     ('info', 'Info'),
)
class Announcement(models.Model):
    """
    Model to hold global announcements
    """
    body = models.TextField(blank=False)
    display = models.BooleanField(default=False)
    level = models.CharField(max_length=7,
                choices=LEVEL_CHOICES, default='info')

    def __unicode__(self):
        return self.body[:50]

For the admin part we can use the defaults to keep it simple. The only extra detail we will add is the information to the object list, to make it easier to know the status of the announcements.

from django.contrib import admin
from .models import Announcement


@admin.register(Announcement)
class AnnouncementAdmin(admin.ModelAdmin):
    list_display = ('body', 'level', 'display')

Easy to access: Context Processor

We want a global announcement, which is made easily with Django’s context processor, which adds whatever we want to the context of every request in our application.

First we create our context processor, which retrieves all the messages that are to be shown. We just need to create a context_processors.py file in our application:

from .models import Announcement


def announcement_context_processor(request):
    """
    Adds the active announcements to the context
    """
    return {
        'announcements': Announcement.objects.filter(display=True)
    }

And we add this context processor to our settings:

TEMPLATE_CONTEXT_PROCESSORS = (
    # Your other context processors
    path.to.context_processors.announcement_context_processor,
)

And that’s all we need to have our announcements on every request. All that’s left to do now is to display them.

Fast to display: Django Template

To display our new global announcements we need to add them to our base template. That is pretty easy:

{% for announcement in announcements %}
  <div class="alert alert-{{ announcement.level }}">{{ announcement.body|safe }} </div>
{% endfor %}

Note that we use the bootstrap classes for alert to change the way the messages are displayed depending on the severity of the message. You could add conditionals or any classes you want here.
It is important to note that we use the filter safe for the body of our announcement. That filter allows us to use html from our backend in case we want to add links or some other html to our messages, but if you don’t think you will need that, you can just remove it.

There are two caveats so far. The first one is that we must not use the name announcement in any context, because it will be overridden in our context processor. Just something to have in mind when naming your context data.

The second one requires more attention: The context processor gets called every request, and it has a database query. This means that we will be hitting our database pretty often for data that will not change. Fortunately this is pretty easy to fix: cache to the rescue!

 

{% cache 300 announcements_cache %}
  {% if announcements %}
    {% for announcement in announcements %}
      <div class="alert alert-{{ announcement.level }}">{{ announcement.body|safe }}</div>
    {% endfor %}
  {% endif %}
{% endcache %}

Our template fragment will be cached and will only hit the database once every 5 minutes, way better. Feel free to play around with the time if you think it is not enough or too much.

Conclusion

We have a simple system for global announcements to send information to our users. It can be edited from the backend and is simple enough to be understood even by non technical staff.

There are several third party apps that can do more complex things, but may be overkill sometimes. If you need something simple all you need to create is an app and edit 4 files and you are set!