Django Signals & django-fieldsignals

default_app_config = 'app.apps.MyApp'

# -*- coding: utf-8 -*-
from django.apps import AppConfig

class MyApp(AppConfig):
    name = 'my_app'
    verbose_name = 'MyApp'

    def ready(self):
        from . import signals

@receiver(post_save_changed, sender=models.WorkEntry, fields=[
], dispatch_uid="update_gitlab_time_spent_updated")
def update_time_spent_on_changed_issue(sender, instance, changed_fields, **kwargs):
     for field, (old, new) in changed_fields.items():
         if == 'issue':
             # do something

A simple and solid approach at deferring javascript execution

In most cases javascript code should be deferred so it doesnt block the page loading process.

Javascript files can be deferred simply like this, the execution order of deferred scripts will be strictly kept by browsers in the order of appearance in the HTML file:

<script defer="defer" src="vendor.js"></script>
<script defer="defer" src="app.js"></script>

However projects might require inline js code. That code will execute before the deferred scripts – which might throw errors if the inline js code depends on libraries loaded via deferred script tags. Inline js code can be deferred like described on stackoverflow:

    window.addEventListener('DOMContentLoaded', function() {
        (function($) {

defer was introduced before async. Its execution starts after parsing is completely finished, but before the DOMContentLoaded event. It guarantees scripts will be executed in the order they appear in the HTML and will not block the parser.

Once all of your js is either triggered by DOMContentLoaded or has the `defer tag it is easy to manage and maintain control of the execution order.

Vuejs + Google Spreadsheets as (simple) Backend

This is a proof of concept to get data from a google spreadsheet via frontend only and display it in a vuejs app, deployed via gitlab pages:

The purpose is to use Google Spreadsheet as a “cheap” data source for a widget on a website.

The project source is public:

Interactions and Goal Tracking with Google Tag Manager – Technical Best Practices for Developers

As you know, Google Tag Manager is a tool that helps Online Marketing people to set up different kind of user interaction and conversion tracking without the help of developers. This separation of roles is beneficial for both developers (no tedious updates of click events) and marketing staff (no dependency to IT), however it only works if web developers prepare the website according to best practices.

This is a document for developers to understand better the life of an Online Marketing staff that is in charge of setting up and maintaining Google Tag Manager click events.

So what do Online Marketing people want to do with Google Tag Manager?

Here is a typical Google Tag Manager (GTM) use case:

  • GTM managers would like to set up the tracking of a range of goals in Google Analytics so that business can see interaction and conversion trends and online marketing can optimise their campaigns based on these signals.
  • GTM managers would like to set this up in Google Tag Manager so that online marketing and business can wire the conversion and interaction signals to their tags and update the wiring quickly and independently from website deployment schedules.

Typical GTM Troubles that Online Marketing Managers have

Below is a list of technical things that sometimes make the life of a GTM manager tough.

1) Link Clicks Triggers don’t fire

  • Sometimes, GMT managers set up click triggers for buttons and the click triggers don’t fire.
  • Here is an example of a typical click trigger on a submitt button on a page like /booking?date=2019-01-01:
  • This is the button:
  • Here is the markup of the button:
  • However the GTM manager notices that this click trigger doesn’t fire.
  • Note: The button click tracking does fire if GTM managers configures the trigger with the button text only (Click Text) == ‘SEND REQUEST’
  • In this specific case GTM managers would like to be able to use the parent element (button with type == submit) because this is less ambiguous and more bullet proof and simpler to set up and maintain.
  • In such cases, when this doesn’t work as intended, maybe this is the cause:
  • Developers can help GTM managers having a simpler and more solid tracking setup by letting click events bubbling up in all cases.

2) No ids or distinguishing classes for important interaction elements

  • Currently important interaction elements such as buttons are not easily identifiable by business / online marketing staff when working with GTM.
  • An ID or a class name like “booking-date-time-step-continue” would simplify tracking setup.
  • Developers can help us simplify tracking by proactively setting ids or identifiable classes on important interaction elements.

3) No dataLayer information for transactional events

  • For some tracking cases GTM managers would like to measure more than a click. Example: a booking. In this case, it would be safer to get a signal from the backend that the booking has correctly been processed and is valid. This helps us prevent wrong trackings, for example if GTM managers would track the click on the ‘book now’ button, and there were form validation errors, GTM managers would still track a successful booking even if the website would not.
  • In order to help us track conversions correctly, you can use Google Tag Manager’s dataLayer.push() method.
  • Example:  dataLayer.push({'event': 'event_name'});
  • See documentation here:
  • Use a speaking and unique event name such as “booking-button-send-request” (similar to html ids)
  • You can push additional information by adding arbitrary properties and values to the object.
  • It helps the GTM managers if you document these event names and additional attributes in a Google spreadsheet that you share with them.

djangocms / djangocms-filer: 500 server error: failed to generate icons for file

This is a nasty error, because django-filer just returns this error message in json format when uploading a file via FilerImageField or the djangocms-filer admin interface.

In order to find out the true reason behind this, set the following env vars to true:


FILER_DEBUG will return a real python exception in the response to the async request that handles the file upload.

In my case, the true error was: decoder jpeg not available which I handled via

That helped!

Responsive Images and Safe Areas

Sometimes a designer or a content editor wants images, videos or more advanced content sections such as image sliders to take the full width of the browser window or even the full screen (height and width).

Full width or full screen content has many advantages from a design point of view and is often used to show off great videos or images in the header of a page. Homepages often use a full screen hero section:

These full width elements normally have a background video or image which is set to cover all the available space, given a fixed height (for example 600px height).

This means, that depending on the browser windows size, a part of the background image will be cut off.

Background images are often centered. This means, that if the browser window is wider than the background image width, an equal part on the top and bottom of the background image will be “cut off” (= not visible). If the browser window is less wide than the background image, the background image will still try to cover the given height of the full-width section. For this, it has to sacrifice space in the horizontal dimension. The background image will therefore be cut off on the left and right, like, for example on this iphone X screen:

In the real world, users will come to see your beautiful full width section on many different screen and browser sizes. Therefore, many of them will never see all of your beautiful background picture, but always just parts of it.

That’s where safe areas come in. A safe area is the part of the image that will always* show, whatever the user’s device or screen size. Here is an example of the definition of a safe area for a specific website project:

Content editors and designers should be briefed accordingly, so that they know how to resize and crop their images, so that the important part of the image is inside the safe area. Safe area briefings for different full-width sections on the website are therefore often created by project managers and web programmers.

Here you are a template that you can download and use for your own purposes:

I hope I could explain the complexities of using full-width images in this articles. If you have any questions don’t hesitate to comment or write to me.

*In practice, of course, there will always be users that wont even see the safe area, because, for example, they use a very old Blackberry phone. However in web design its best practice to set a compatibility policy and then ignore devices and browsers outside of that policy for cost and efficiency reasons.

Django<2 and sqlite3 Incompatibility

Older versions of django are currently not compatible with sqlite>3.25. More information here: – Problems occur when trying to load a fixture via `./ loaddata` or in some cases when using a database created in an old version of sqlite3 django throws a segmentation fault.

Uninstall any existing homebrew versions of sqlite3 via brew uninstall sqlite3

Then install sqlite 3.35:

brew install

Django / DjangoCMS – custom language

We had to create custom languages in Django for two different versions of the Chinese language: yue (Mandarin Chinese) and cmn (Cantonese Chinese)

This is really simple in Django with gettext, as the default languages can just be overriden:

    ## Customize this
    ('en', "English"),
    ('cmn', "Mandarin Chinese"),
    ('yue', "Cantonese Chinese"),

    ## Customize this
    1: [
            'code': 'en',
            'name': gettext('en'),
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
            'code': 'cmn',
            'name': "Mandarin Chinese",
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
            'code': 'yue',
            'name': "Cantonese Chinese",
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
    'default': {
        'redirect_on_fallback': True,
        'public': True,
        'hide_untranslated': False,

updating from templates and python code:

./ makemessages -l en
./ makemessages -l yue
./ makemessages -l cmn

"Plural-Forms: nplurals=2; plural=(n != 1);\n" needs to be part of every .po file for DjangoCMS

./ compilemessages

Use Google Sheets as a database for a frontend widget

Here is an example, parts of this come from a Stackoverflow article, but I can’t find the link to it anymore. If you find the SO article, please kindly let me know so I can reference the original source here.

<!doctype html>
<html class="no-js" lang="">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css" />

<!--[if lt IE 8]>
You are using an <strong>outdated</strong> browser. Please <a href="">upgrade your browser</a> to improve your experience.

<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>

function query_spreadsheet(query_string) {
    var api = {
        baseUrl: '',
        sheet_key: "19YCYRQuuXWaQYVr-lMAeufIPFkp7lH24xwdL_Htbbu8",
        gid: '0',
        headers: '1'
    var random_id = Math.random().toString(36).substring(2);
    window.last_search_call = random_id;
    var random_callback_handler = "callback_func_" + random_id;
    let sql_query = "select A,B,C,D,E,F where A matches '" + query_string + ".*' or LOWER(B) contains '" + query_string + "'";
    let request_url = api.baseUrl + api.sheet_key + "/gviz/tq";
        url: request_url,
        // The name of the callback parameter, as specified by the YQL service
        jsonp: false,
        jsonpCallback: random_callback_handler,
        // Tell jQuery we're expecting JSONP
        dataType: "jsonp",
        data: {
            headers: api.headers,
            gid: api.gid,
            tqx: "responseHandler:" + random_callback_handler,
            tq: sql_query,
        success: function( response ) {
            if (window.last_search_call === random_id) {
                var result = []
                if (response.table.rows.length) {
                    var first_row = response.table.rows[0]
                    result = [
                            'id': 0,
                            'zip': first_row.c[0].v,
                            'city': first_row.c[1].v,
                            'canton': first_row.c[2].v,
                            'name': first_row.c[3].v,
                            'phone': first_row.c[4].v,
                            'img_url': first_row.c[5].v
                console.log( result ); // server response 
            } else {
                console.log('call cancelled!')
        error: function(XHR, textStatus, errorThrown) {
            console.log("error: " + textStatus);
            console.log("error: " + errorThrown);
jQuery(document).ready(function() {
    jQuery('body').on('click', '#sales-reps-search-button', function(ev) {
        // get query string form input field
        var searchString = jQuery('#sales-reps-search-field').val();
        var trimmedSearchString = searchString.trim();
    <input type="text" value="8000" class="form-control" id="sales-reps-search-field" placeholder="Search ...">
    <button id="sales-reps-search-button">Search!</button>

<div class="result">
<div class="error">


django language switcher

In Django, it’s not evident how to properly switch languages. Google searches result in many different approaches, but none of them is clean. In django-cms however, there is a clean and supported approach: the language_chooser template tag.