Electron logo

Django esta diseñado, construido y enseñado para trabajar en un servidor. Su naturaleza es backend. Pero, aunque pareza mentira, también puede ser utilizado para desarrollar aplicaciones multiplataforma. ¿Como? Con la ayuda de Electron.

Electron es una framework desarrollado por GitHub para el desarrollo de aplicaciones de escritorio. Permite trabajar con herramientas web de frontend (HTML, CSS y Javascript). El truco reside en que ejecuta Node por detrás pero conservando el aspecto de una aplicación nativa. Sin duda nos encontramos ante una herramienta muy potente con un sin fin de posibilidades.

La guía que dejo a continuación es compatible con cualquier aplicación de Python. Puede ser utilizada para tener una GUI en HTML/CSS y un corazón de Python. Y, por supuesto, usado con otros Frameworks webs como Flask.

Paso 1: Instalando Electron

git clone https://github.com/electron/electron-quick-start mi-app-django
cd mi-app-django

Abrimos package.json con nuestro editor favorito.

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "repository": "https://github.com/electron/electron-quick-start",
  "keywords": [
    "Electron",
    "quick",
    "start",
    "tutorial",
    "demo"
  ],
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "^1.4.1"
  }
}

Y añadimos al final las dependencias que necesitaremos.

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "repository": "https://github.com/electron/electron-quick-start",
  "keywords": [
    "Electron",
    "quick",
    "start",
    "tutorial",
    "demo"
  ],
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "^1.4.1"
  },
  "dependencies": {
    "request": "*",
    "request-promise": "*"
  }
}

Instalamos.

npm install && npm start

Cuando termine se abrirá la siguiente ventana.

Electron hello world

Paso 2: Configurando Electron

Debemos decirle a Electron que ejecute Django y nos lo muestre en la ventana.

Abrimos main.js y lo actualizamos. Solo me modificado el interior de createWindow().

const electron = require('electron')
    // Module to control application life.
const app = electron.app
    // Module to create native browser window.
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')
    // Keep a global reference of the window object, if you don't, the window will
    // be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow() {

	var subpy = require('child_process').spawn('python3', ['./manage.py', 'runserver']);
    var rq = require('request-promise');
    var mainAddr = 'http://localhost:8000';
    var openWindow = function() {
        mainWindow = new BrowserWindow({ width: 800, height: 600 })
        mainWindow.loadURL('http://localhost:8000');
        mainWindow.on('closed', function() {
            mainWindow = null;
            subpy.kill('SIGINT');
        })
    }
    var startUp = function() {
        rq(mainAddr)
            .then(function(htmlString) {
                console.log('Django iniciado!');
                openWindow();
            })
            .catch(function(err) {
                startUp();
            });
    };
    startUp();
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
    // Quit when all windows are closed.
app.on('window-all-closed', function() {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit()
    }
})
app.on('activate', function() {
        // On OS X it's common to re-create a window in the app when the
        // dock icon is clicked and there are no other windows open.
        if (mainWindow === null) {
            createWindow()
        }
    })
    // In this file you can include the rest of your app's specific main process
    // code. You can also put them in separate files and require them here.

Paso 3: Instalando Django

pip install django
django-admin startproject proyecto_ejemplo .

Paso 4: Ejecutando

electron .

¡Y listo!

Django y electron

Conclusiones

Si quieres distribuirla deberás usar pyinstaller para empaquetar Python y tus dependencias en un ejecutable. Y sustituir.

var subpy = require('child_process').spawn('python3', ['./manage.py', 'runserver']);

por

var subpy = require('child_process').spawn('.dist/app-django.exe', ['./manage.py', 'runserver']);

La única limitación veo es la base de datos. En local tendrás que limitarte a SQLite. Aunque si que podrás conectar Django con una externa. Pero bueno, más que suficiente para construir cualquier aplicación. También debes ser consciente de las limitaciones propios del navegador: bajo rendimiento y scrolling entre otras cosas. Y sería recomendable suavizar las transiciones con algún Framework de Javascript. Pero por el resto… ¡¡ancha es castilla!!