Brainstorm's snippets (20/243)

  How to enable Screen Sharing on Macs via Terminal
  Easy Printing
python, print
  Redirect to an arbitrary URL After Saving in Django Admin
django, admin, redirect
  Check reverse relation for OneToOneField
  django-selectable usage (example)
  Automatically zip a FileField upon saving in Django
filefield, zip
  Cherry-picking a GIT commit from another git repository
  Sortable column in ModelAdmin.list_display on a calculated property
  CSS: How To Center an Object Exactly In The Center
  Git partial cherry-picking
  How to float a <label> to the left of an <input>?
  Django delete FileField
  PUB/SUB in javascript
javascript, pubsub
  pyclean alias
  How to Install Node.js and NPM on a Mac
mac, npm, nodejs
  Monit guide (updated)
  Flat list out of list of lists in Python
  Raspberry PI setup guide
setup, raspberrypi
  Run ssh-agent and ssh-add on login via SSH
  Management command to dump local db and media
django, management_command

  How to enable Screen Sharing on Macs via Terminal

sudo /System/Library/CoreServices/RemoteManagement/ -activate -configure -access -off -restart -agent -privs -all -allowAccessFor -allUsers


  Easy Printing

row = [100, "android", "ios", "blackberry"]
print(', '.join(str(x) for x in row))
print(*row, sep=', ')


  Redirect to an arbitrary URL After Saving in Django Admin

Put following change_view-method in your and now you can make links to admin change page that return you back to where you came from:

class BlogEntryAdmin(admin.ModelAdmin):


    def change_view(self, request, object_id, form_url='', extra_context=None):
        result = super(BlogEntryAdmin, self).change_view(request, object_id, form_url, extra_context)
        if request.GET.get('return_to', False):
            result['Location'] = request.GET['return_to']
        return result

In your template you'd have something like:

{% if request.user.is_staff %}
    <a href="/admin/blog/blogentry/{{ }}/?return_to={{ entry.get_absolute_url }}">Edit in admin</a>
{% endif %}

  Check reverse relation for OneToOneField

If related object does not exist, any direct reference to it produces the exception:

RelatedObjectDoesNotExist: Contatore has no pratica.

To avoid it, explicitly test the existence of the attribute:

class Contatore(BaseModel):


    def is_istallato(self):
        return hasattr(self, 'pratica') and self.pratica is not None


class Pratica(BaseModel):


    contatore = models.OneToOneField(Contatore, null=True, blank=True, on_delete=models.SET_NULL)


  django-selectable usage (example)

Include django-selectable to the requirements:


jQuery-UI is needed:

npm install jquery-ui-dist

Add selectable to your INSTALLED_APPS:


Add the urls to your root url patterns:

urlpatterns = [
    path('selectable/', include('selectable.urls')),

Inclusions in file base.html:

<script src="{% static 'jquery/dist/jquery.min.js' %}" type="text/javascript"></script>
<script src="{% static 'jquery-ui-dist/jquery-ui.min.js' %}" type="text/javascript"></script>

<script src="{% static 'selectable/js/' %}" type="text/javascript"></script>

Define the lookup view:


from selectable.base import ModelLookup
from selectable.registry import registry
from backend.models import Contatore

class ContatoreLookup(ModelLookup):
    model = Contatore
    search_fields = ('seriale__icontains', )
    filters = {}

    def get_query(self, request, term):
        self.filters['azienda__codice'] = request.GET.get('azienda', '')
        results = super(ContatoreLookup, self).get_query(request, term)
        return results

    def get_item_label(self, item):
        n = 40
        nome = item.nome
        if len(nome) > n:
            nome = nome[:n] + "..."
        return "%s [%s]" % (item.codice, nome)

    def get_item_value(self, item):
        return item.codice


Use AutoCompleteWidget in the form field:


from django import forms
from selectable.forms import AutoCompleteWidget
from .lookups import ContatoreLookup

class SelectContatoreForm(forms.Form):

    contatore = forms.CharField(
        widget=AutoCompleteWidget(ContatoreLookup, limit=10),

Usage in a modal

You need to call this after form loading:



Tracing selectable events and advanced filtering

function trace_djevents(element) {
    element.on('djselectablecreate', function(event) { console.log('EVENT: djselectablecreate'); });
    element.on('djselectablesearch', function(event) { console.log('EVENT: djselectablesearch'); });
    element.on('djselectableopen', function(event) { console.log('EVENT: djselectableopen'); });
    element.on('djselectablefocus', function(event) { console.log('EVENT: djselectablefocus'); });
    element.on('djselectableselect', function(event) { console.log('EVENT: djselectableselect'); });
    element.on('djselectableclose', function(event) { console.log('EVENT: djselectableclose'); });
    element.on('djselectablechange', function(event) { console.log('EVENT: djselectablechange'); });

function after_form_load() {


    var azienda = $('#id_azienda');
    var cantiere = $('#id_cantiere');


    cantiere.djselectable('option', 'prepareQuery', function(query) {
        query.azienda = azienda.val();

    cantiere.on('djselectableselect', function(event) {
        var url = sprintf('/api/cantiere/%s/%s/', azienda.val(), cantiere.val());
            type: 'GET',
            url: url,
            dataType: 'json',
            data: $(this).serialize(),
            success: function(data) {

    azienda.on('change', function(event) {
        console.log('azienda changed');

    setTimeout(function() { cantiere.focus() }, 500);

Sample .ui-autocomplete styling (.scss)

.ui-autocomplete {
    // position: absolute;
    // z-index: 3000 !important;
    //cursor: default;

    background-color: white;
    list-style: none;
    padding: 0 8px;
    font-size: 18px;
    border: 1px solid #ccc;

    .ui-menu-item {
        padding: 4px;
        a {
            color: #666;
        .highlight {
            background-color: yellow;

  Automatically zip a FileField upon saving in Django

(c) 2019 Mario Orlandi, Brainstorm S.n.c.

Automatically zip a FileField upon saving in Django

__author__    = "Mario Orlandi"
__copyright__ = "Copyright (c) 2019, Brainstorm S.n.c."
__license__   = "GPL"

import os
import zipfile
from import FileSystemStorage

class ZipFileStorage(FileSystemStorage):
    A specialized FileSystemStorage which, upon saving the file,
    transparently compresses it into a zip archive (if not already zipped)

    Sample usage:

        source_data = models.FileField(..., storage=ZipFileStorage())

    def save(self, name, content, max_length=None):

        # Let the base storage class save the file as usual
        name = super().save(name, content, max_length)

        # Check if already zipped
        if not name.lower().endswith('.zip'):

            # If not, create a new zip archive and write the file into it
            path = self.path(name)
            zf = zipfile.ZipFile(path + '.zip', mode='w', compression=zipfile.ZIP_DEFLATED)
                zf.write(path, os.path.basename(path))

            # Cleanup: the orginal unzipped file can now be removed

            # The new return value will be the name of the zip archive just created
            name += '.zip'

        return name

  Cherry-picking a GIT commit from another git repository

This script applies locally the diff of a commit from another repo; "apply" doesn't commits the patch, so you have to supply a commit message yourself

git --git-dir=../[some_other_repo]/.git show [commit_SHA] | git apply

The following script also commits the patch:

git --git-dir=../[some_other_repo]/.git format-patch -k -1 --stdout [commit_SHA] | git am -3 -k --ignore-whitespace


  • The git format-patch command creates a patch
  • from [some_other_repo]'s commit
  • specified by its [commit_SHA]
  • "-1" for one single commit alone
  • "-k" keeps the subject (don't add [PATCH])
  • This patch is piped to git am, which applies the patch locally
  • "-3" means trying the three-way merge if the patch fails to apply cleanly
  • "-k" keeps the subject (don't strip [PATCH])


  Sortable column in ModelAdmin.list_display on a calculated property

class DeAdmin(admin.ModelAdmin):
    list_display = ("[...]", "s_d", "gd", "na", "de", "fr" )

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        queryset = queryset.annotate(
                When(fr=True, then='s_d'),
                When(fr=False, then=F('gd') + F('na')),
        return queryset

    def s_d(self, obj):
        return obj._s_d
    s_d.admin_order_field = '_s_d'


  CSS: How To Center an Object Exactly In The Center

.centered {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

To center text in a div, set line-height equal to height:

.center {
    height: 400px;
    line-height: 400px; /* same as height! */


  Git partial cherry-picking

git cherry-pick -n <commit> # get your patch, but don't commit (-n = --no-commit)
git reset                   # unstage the changes from the cherry-picked commit
git add -p                  # make all your choices (add the changes you do want)
git commit                  # make the commit!

It looks like there's a new way to do this with interactively checking out:

git checkout -p bc66559


  How to float a <label> to the left of an <input>?

<div class="field">
    <input type="text">
.field {
    display: table;
    width: 100%;

.field > label,
.field > input {
    display: table-cell;

.field > input {
    width: 80%;


.field {
    display: table;
    width: 100%;

.field > label,
.field > input {
    display: table-cell;

.field > label {
    text-align: right;
    font-weight: bold;
    padding-right: 8px;

.field > label:after {
    content: ':';

.field > input {
    width: 80%;


  Django delete FileField

import os
from django.db import models
from django.dispatch import receiver

def remove_file_and_cleanup(filepath):
    Helper to remove a media file;
    also removes the containing folder, if left empty
    folder = os.path.dirname(filepath)
    # remove file
    if os.path.isfile(filepath):
    # finally, remove folder if empty
    if os.path.isdir(folder) and len(os.listdir(folder)) <= 0:

@receiver(models.signals.post_delete, sender=MyModel)
def auto_delete_file_on_delete(sender, instance, **kwargs):
    """ Deletes file from filesystem when corresponding `MediaFile` object is deleted.
        Adapted from:

    # Collect names of FileFields
    fieldnames = [ for f in instance._meta.get_fields() if isinstance(f, models.FileField)]
    for fieldname in fieldnames:
        field = getattr(instance, fieldname)
        if bool(field):

@receiver(models.signals.pre_save, sender=MyModel)
def auto_delete_file_on_change(sender, instance, **kwargs):
    """ Deletes file from filesystem when corresponding object is changed or removed.
        Adapted from:

    if not or sender.objects.filter( <= 0:
        return False

    fieldnames = [ for f in instance._meta.get_fields() if isinstance(f, models.FileField)]
    for fieldname in fieldnames:
            old_field = getattr(sender.objects.get(, fieldname)
            old_filepath = old_field.path

            new_field = getattr(instance, fieldname)
            new_filepath = new_field.path if new_field else None

            # if ready to save a new file, delete the old one
            if old_filepath != new_filepath:

  PUB/SUB in javascript

file publisher.js:

function initialize_datatable(element, url) {

        type: 'GET',
        url: url + '?action=initialize',
        dataType: 'json'
    }).done(function(data, textStatus, jqXHR) {

        var table = ...

        // Notify subscribers
            'datatableInitialized', [table]



file sample_subscriber.html:

<div class="panel panel-danger subscribe-datatable-initialized" id="stale-acquisition-panel" style="display: none;">

    <p>This panel is to be shown only after datatable has been initialized</p>


<script language="javascript">

    $(document).ready(function() {
        $('#stale-acquisition-panel').on('datatableInitialized', function(e, eventInfo) {


  pyclean alias

alias pyclean="find . \( -name \*.pyc -o -name \*.pyo -o -name __pycache__ \) -prune -exec rm -rf {} +"

  How to Install Node.js and NPM on a Mac

Install node with homebrew:

brew install node

To see if Node is installed:

node -v

To see if NPM is installed:

npm -v

How to Update Node and NPM

brew update
brew upgrade node

How to Uninstall Node and NPM

brew uninstall node

Downgrading Node/NPM

To install a specific NodeJS version with brew, see:

To install a specific NPM version in case NPM is already installed (meaning, to upgrade or downgrade the installed NPM version) you can use NPM itself !

# install a specific NPM version,
# with the `-g` / `--global` flag to replace the previously installed version
npm install -g npm@4


How to use

Then in your project you can finally populate node_modules from package.json as follows:

npm install

How to Install Sass compiler on a Mac

Check if ruby is available:

ruby -v

If not, install it:

brew install ruby

Install Sass:

sudo gem install sass

In case of SSL error "SSL Error When installing rubygems, Unable to pull data from '", try this:

sudo gem sources -r
sudo gem sources -a

the again:

sudo gem install sass

Eventuale aggiornamento dei requirements:

pip install -r requirements/development.txt

  Monit guide (updated)

Install Monit System Monitor On Ubuntu 18.04

sudo apt update
sudo apt install monit

After installing Monit, the commands below can be used to stop, start and enable Monit service:

sudo systemctl stop monit.service
sudo systemctl start monit.service
sudo systemctl enable monit.service

Configure Monit service

By default all files located on /etc/monit/conf.d/ and /etc/monit/conf-enabled/ are read by monit when the service is started.

Use the /etc/monit/conf.d/ directory to put all your monitoring configuration files in it.

Example: file /etc/monit/conf.d/disk:

check device root with path /
    if SPACE usage > 80% then alert

check device backup_disk with path /mnt/backup
    if SPACE usage > 80% then alert


sudo monit check -t
sudo monit reload
sudo monit start all

Check Monit status

sudo monit status

Access Monit Web Portal

Add this to file /etc/monit/monitrc:

set httpd port 2812
    allow admin:password

then access the web page from anywhere with:




  Flat list out of list of lists in Python

Given superlist as a list of lists,

flat_list = [item for sublist in superlist for item in sublist]

which means:

flat_list = []
for sublist in superlist:
    for item in sublist:


  Raspberry PI setup guide

1   Initial setup

Eventually expand file system if part of the SD is unused:

raspi-config --expand-rootfs

2   Enable SSH connections

Connect with a keyboard, then issue these commands from terminal:

systemctl enable ssh.service
systemctl start ssh.service

3   Set locale

sudo -s
export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
locale-gen en_US.UTF-8
dpkg-reconfigure locales

5   Expand swap partition ...

100 Mb (the default) is too small, and if you are doing memory intensive stuff (e.g., web surfing), you can easily max it out.

The recommended swap size is 2*physical RAM - in the case of the Pi (modern/current versions of), this is 2G.

You can increase the swap size by changing the CONF_SWAPSIZE or the CONF_SWAPFACTOR parameter in /etc/dphys-swapfile followed by a reboot:

cat /etc/dphys-swapfile


Test after reboot:

# free -h
              total        used        free      shared  buff/cache   available
Mem:           927M        259M        425M         30M        242M        585M
Swap:          1.8G          0B        1.8G

6   ... or remove it !

On the other hand, you might want to turn off swap entirely in order to reduce the amount of write operations on the SD card – because SD cards have their life limited to the amount of write operations:

sudo systemctl disable dphys-swapfile

Test after reboot:

# free -h
              total        used        free      shared  buff/cache   available
Mem:           927M        257M        437M         24M        232M        593M
Swap:            0B          0B          0B

Another option is to move swap to an external device (for example a USB key).


7   Setup SMTP

TODO; vedere:

Installazione e Configurazione di Postfix su Raspberry usando come Smarthost GMAIL:


8   Start chromium in Kiosk mode on raspbian jessie

file ~/Desktop/runChromium.desktop:

[Desktop Entry]
Exec=/usr/bin/chromium-browser --noerrdialogs --disable-session-crashed-bubble --disable-infobars --kiosk
Comment=Start Chromium in kiosk mode; copy int ~/.config/autostart to have it run automatically


9   Pi display

How to hide the cursor in the kiosk mode automatically:

sudo apt-get install unclutter

then add this to file /etc/xdg/lxsession/LXDE/autostart:

@unclutter -idle 0.1 -root

10   Display rotation

file "/boot/config.txt":

# LCD Rotation

# Display Rotate (HDMI)

11   Disable screen sleep

file "/etc/lightdm/lightdm.conf":

# don't sleep the screen
xserver-command=X -s 0 dpms


sudo apt-get install xscreensaver

then configure the screensaver application under the Preferences option on the main desktop menu.

12   How to up a Static IP on Your Ethernet or Wireless Network Connection

file /etc/dhcpcd.conf:

# setup ethernet static ip
interface eth0
static routers=

# setup wireless static ip
interface wlan0
static routers=


# setup ethernet static ip
interface eth0
static ip_address=
static routers=
static domain_name_servers=


#sudo /etc/init.d/networking restart
sudo reboot

13   Setting up a Raspberry Pi as an access point in a standalone network (NAT)

Brief summary:

Install required software:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install dnsmasq hostapd

then stop the services and reboot:

sudo systemctl stop dnsmasq
sudo systemctl stop hostapd
sudo reboot

Configuring a static IP for wlan0:

add this to /etc/dhcpcd.conf:

interface wlan0
    static ip_address=
    nohook wpa_supplicant


sudo service dhcpcd restart

Configuring the DHCP server (dnsmasq):

sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
sudo vim /etc/dnsmasq.conf

and add:

interface=wlan0      # Use the require wireless interface - usually wlan0

Configuring the access point host software (hostapd):

add this to /etc/hostapd/hostapd.conf:



  • NETWORK_PASSPHRASE: between 8 and 64 characters

  • hw_mode:

    • a = IEEE 802.11a (5 GHz)
    • b = IEEE 802.11b (2.4 GHz)
    • g = IEEE 802.11g (2.4 GHz)
    • ad = IEEE 802.11ad (60 GHz)

Add this to /etc/default/hostapd:


Start the access point host software:

sudo systemctl start hostapd
sudo systemctl start dnsmasq

if necessary:

sudo systemctl unmask hostapd
sudo systemctl enable hostapd
sudo systemctl start hostapd

Add routing and masquerade:

Uncomment this line in /etc/sysctl.conf:


Add a masquerade for outbound traffic on eth0:

sudo iptables -t nat -A  POSTROUTING -o eth0 -j MASQUERADE

Save the iptables rule:

sudo sh -c "iptables-save > /etc/iptables.ipv4.nat"

Edit /etc/rc.local and add this just above "exit 0" to install these rules on boot:

iptables-restore < /etc/iptables.ipv4.nat

Reboot and done !


Setting up a Raspberry Pi as an access point in a standalone network (NAT)

15   Installing Python 3.7.x on Raspbian

sudo su
mkdir downloads
cd downloads

apt-get update -y
apt-get install build-essential tk-dev libncurses5-dev libncursesw5-dev libreadline6-dev libdb5.3-dev libgdbm-dev libsqlite3-dev libssl-dev libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev libffi-dev -y
tar xf Python-3.7.2.tar.xz
cd Python-3.7.2
make -j 4
make altinstall
cd ..
rm -r Python-3.7.2
rm Python-3.7.2.tar.xz
apt-get --purge remove build-essential tk-dev libncurses5-dev libncursesw5-dev libreadline6-dev libdb5.3-dev libgdbm-dev libsqlite3-dev libssl-dev libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev libffi-dev -y
apt-get autoremove -y
apt-get clean


16   todo

All my Pi's (including Pi Zero's) have these two lines added to /etc/fstab:

tmpfs /tmp tmpfs defaults,noatime,nosuid 0 0
tmpfs /var/log tmpfs defaults,noatime,nosuid,size=16m 0 0

The default maximum size is half the memory, but of course tmpfs only takes as much memory as the files need.

While you are in /etc/fstab, if you want to reduce writes to the SD then the other simple change is the flush rate.

For the ext4 / mount make sure the options include "commit=600" That is, for example:

PARTUUID=e96d960e-02 / ext4 defaults,noatime,commit=600,errors=remount-ro 0 1

Obviously do not do this if your site is prone to power cuts or other unexpected outages. (dirty pages in the disk cache are written out every ten minutes instead of every five seconds which is the default). This improves performance as well.

  Run ssh-agent and ssh-add on login via SSH

You can try adding this:

eval $(ssh-agent -s)
ssh-add ~/.ssh/id_rsa

This way the ssh-agent does not start a new shell, it just launches itself in the background and spits out the shell commands to set the appropriate environment variables.

As said in the comment, maybe you do not want to run the agent at all on the remote host, but rather on the box you are working from, and use:

ssh -A remote-host

to forward the services of your local ssh agent to the remote-host.

For security reasons you should only use agent forwarding with hosts run by trustworthy people, but it is better than running a complete agent remotely any time.

  Management command to dump local db and media

file management/commands/


usage: dump_local_data [-h] [--version] [-v {0,1,2,3}]
                                 [--settings SETTINGS]
                                 [--pythonpath PYTHONPATH] [--traceback]
                                 [--no-color] [--dry-run] [--max-age MAX_AGE]
                                 [--no-gzip] [--legacy]

Dump local db and media for backup purposes (and optionally remove old backup

positional arguments:
  target                choices: db, media, all

optional arguments:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  -v {0,1,2,3}, --verbosity {0,1,2,3}
                        Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings SETTINGS   The Python path to a settings module, e.g.
                        "myproject.settings.main". If this isn't provided, the
                        DJANGO_SETTINGS_MODULE environment variable will be
  --pythonpath PYTHONPATH
                        A directory to add to the Python path, e.g.
  --traceback           Raise on CommandError exceptions
  --no-color            Don't colorize the command output.
  --dry-run, -d         Dry run (simulation)
  --max-age MAX_AGE, -m MAX_AGE
                        If > 0, remove backup files old "MAX_AGE days" or more
  --no-gzip             Do not compress result
  --legacy              use legacy Postgresql command syntax

Code snippet has been moved to: