Need help with your Django project?
Check our django servicesGoogle Analytics is an integral part of almost any website. Integration requires pasting a simple JavaScript in your webpage.
Although it sounds easy, there are some things that we can take in mind:
- Making it more configurable, without overkilling with a standalone Django app.
- Actually disabling google analytics for everything that's not production - we don't want to track fake views.
The setup
TL;DR - we are going to setup a Django project with website
app there.
Everything below is for Python 3.
For our example, we are going to start with a plain Django project, called django_google_analytics
:
$ pip install Django # 1.11 by the time of writing
$ django-admin startproject django_google_analytics
First things first, we are going to create a Django app called website
and configure everything we need for the example:
$ python manage.py startapp website
Adding the app:
# django_google_analytics/settings.py
# ...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Important line V
'website.apps.WebsiteConfig'
]
# ...
URL configuration:
# django_google_analytics/urls.py
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('website.urls', namespace='website'))
]
# website/urls.py
from django.conf.urls import url
from .views import index
urlpatterns = [
url(r'^$', index, name='index')
]
And view
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
Templates
To lay foundations for google analytics, we are going to start from the templates. We are going to have base.html
and index.html
.
In a snippets/ga.html
we are going to put the script required for having google analytics.
website
├── templates
├── base.html
└── index.html
└── snippets
└── ga.html
In base.html
we are going to define a block for google analytics:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
{% block content %}
{% endblock content %}
{% block ga %}
{% endblock ga %}
</body>
</html>
And for the sake of the presentation, let’s do a simple index.html
:
{% extends "base.html" %}
{% block content %}
<h1>Hello!</h1>
{% endblock content %}
Our snippets/ga.html
is going to contain the following:
<script id="google-analytics" type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'THE_TRACKING_ID_HERE', 'auto');
ga('send', 'pageview');
</script>
In the ga
block we are going to put our google analytics script:
{% block ga %}
{% include "snippets/ga.html" %}
{% endblock ga %}
You can read more about include
here.
Now we have almost everything in place to make things work:
- We need to provide value for
THE_TRACKING_ID_HERE
- We need to show that snippet only “in production”
GA tracking id
According to Google, the tracking id is:
The tracking ID is a string like UA-000000-2. It must be included in your tracking code to tell Analytics which account and property to send data to.
We need to pass that to our template.
If we have that ga_tracking_id
loaded in our template context, we can use template interpolation in snippets/ga.html
:
// ...
ga('create', '{{ ga_tracking_id }}', 'auto');
// ...
and change the include in base.html
as follows:
{% block ga %}
{% include "snippets/ga.html" with ga_tracking_id=ga_tracking_id %}
{% endblock ga %}
Now, lets add settings variable to keep the GA_TRACKING_ID
:
# django_google_analytics/settings.py
# ...
GA_TRACKING_ID = 'UA-YOUR-TRACKING-ID'
# ...
Here, we have options. For example, we can use django-environ
for that. For the sake of simplicity, we are going to use a hardcoded value.
Injecting GA_TRACKING_ID into the template context
Now, we need a way to inject the value of GA_TRACKING_ID
into every Django template that we render.
One option is to do it in the view, but this is going to require every view doing it, leading to a lot of repetition.
Fortunately, there’s a neat feature in Django called context processors
Quoting the documentation:
A context processor has a very simple interface: It’s a Python function that takes one argument, an HttpRequest object, and returns a dictionary that gets added to the template context.
This means – whatever we return from the context processor, it’s going to be available in every template.
We need to write such function and provide a dotted path to it in TEMPLATES[0]['OPTIONS']['context_processors']
.
In a file called website/context_processors.py
lets do that:
# website/context_processors.py
from django.conf import settings
def ga_tracking_id(request):
return {'ga_tracking_id': settings.GA_TRACKING_ID}
A small comment – the name of the file can be variable since we provide the exact path to the function. But for consistency reasons, we call it context_processors.py
.
Now, the last step is to add that ga_tracking_id
as a context processor for our templates.
Change TEMPLATES
settings to the following:
# django_google_analytics/settings.py
# ...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
# Important line V
'website.context_processors.ga_tracking_id'
],
},
},
]
# ...
Now, if we run our Django & open /
we should see that google analytics script is loaded and the value of settings.GA_TRACKING_ID
is there.
Production only
There’s one last thing that we need to do – make sure we put that GA script only in production.
Now the big question here is – how do we know that we are in production? There are many ways to determine that.
That’s why we are going to use a simple feature switch approach – add a boolean USE_GA
in settings.py
and read from env, defaulting to False
. This way, when deploying to production, we can set the right value for the env variable.
In django_google_analytics/settings.py
do the following:
# django_google_analytics/settings.py
# ...
"""
1) os.environ is a dict
2) We are using a simple dict literal to parse the string value to boolean
"""
USE_GA = os.environ.get('DJANGO_USE_GA', False)
USE_GA = {'True': True, 'False': False}.get(USE_GA, False)
A small comment – using django-environ
is a better solution for the code above.
As you can see, we are defaulting to False
by using the second argument of get
Now, we are going to write another context processor, called use_ga
which returns the value of USE_GA
.
In website/context_processors.py
do the following:
# website/context_processors.py
from django.conf import settings
def ga_tracking_id(request):
return {'ga_tracking_id': settings.GA_TRACKING_ID}
def use_ga(request):
return {'use_ga': settings.USE_GA}
Before we use it, we need to add that context processor to template settings:
# django_google_analytics/settings.py
# ...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'website.context_processors.ga_tracking_id',
# Important line V
'website.context_processors.use_ga'
],
},
},
]
# ...
Now, having this in every template, we can finish the GA block in base.html
:
{% block ga %}
{% if use_ga %}
{% include "snippets/ga.html" with ga_tracking_id=ga_tracking_id %}
{% endif %}
{% endblock ga %}
And with this, we are ready. Google analytics is going to show up only in production.
You can test this by running the server with different values:
$ DJANGO_USE_GA=True python manage.py runserver
and
$ DJANGO_USE_GA=True python manage.py runserver
Code quality improvement
As you can see, we have 2 context processors, providing values for 2 different settings.
We can make a simple template tag that fetches values from Django settings like so:
{% get_from_settings 'USE_GA' %}
To do that, we need to create the following structure:
website
├── templatetags
├── __init__.py
└── settings.py
In website/templatetags/settings.py
do the following:
from django import template
from django.conf import settings
register = template.Library()
@register.simple_tag
def get_from_settings(key, default=None):
return getattr(settings, key, default)
Now we are ready to use that if we want to. For example:
{% load settings %}
...
{% block ga %}
{% get_from_settings 'USE_GA' as use_ga %}
{% if use_ga %}
{% include "snippets/ga.html" with ga_tracking_id=ga_tracking_id%}
{% endif %}
{% endblock ga %}
And with that, we are ready.