Esta pagina se ve mejor con JavaScript habilitado

Django Webpack

 ·  ūüéÉ kr0m

En este artículo explicaremos como integrar Django con WebPack y como administrar toda la paquetería JavaScript mediante Yarn, este modo de operar nos brindará una serie de ventajas respecto al desarrollo tradicional ya que WebPack nos permitirá reutilizar código entre módulos JavaScript, un sistema de cacheo inteligente y carga de contenido bajo demanda de partes del código JavaScript.

Antes de comenzar es recomendable leer los artículos anteriores sobre Django ya que son los pasos previos a este artículo:


La integración de Django con WebPack implica varias partes:

  • WebPack: Empaquetador de contenido.
  • Yarn: Administrador de paquetes JavaScript, se jacta de ser m√°s r√°pido, seguro y confiable que NPM.
  • Webpack-bundle-tracker: Plugin de WebPack encargado de generar el fichero webpack-stats.json donde se indica toda la informaci√≥n necesaria para que el paquete django-webpack-loader pueda hacer las sustituciones necesarias en los templates de Django por los nombres de los ficheros de los bundles.
  • Django-webpack-loader: Paquete que leer√° el fichero webpack-stats.json y realizar√° las sustituciones necesarias en los templates por los nombres de los ficheros de los bundles.

WebPack es un empaquetador de contenido web, empaqueta tanto assets como c√≥digo JavaScript, la salida final es un √ļnico fichero JS, de este modo importando este fichero podremos importar hojas de estilo y c√≥digo JS.

Tambi√©n permite “code splitting” y “lazy loading”, as√≠ se cargar√°n las partes del contenido que se pida en ese momento en respuesta a ciertas acciones del usuario.

Por otra parte Webpack resulta muy √ļtil para evitar problemas de cacheo de contenido, los ficheros son generados con un √ļnico hash como nombre del fichero, de este modo cuando se realizan cambios en la web se regeneran dichos hashes, estos son distintos a los hashes previos, por lo tanto un cliente que recargue la web estar√° pidiendo otro fichero distinto y la cach√© del navegador ser√° descartada mostrando el contenido actualizado.

Para utilizar assets en los templates de Django hay que hacerlo por su nombre, el problema es que WebPack genera los ficheros hasheados como se ha descrito anteriormente, para poder utilizar los ficheros generados por WebPack de forma transparente utilizaremos el plugin webpack-bundle-tracker y el paquete django-webpack-loader que har√° las sustituciones oportunas en nuestros templates con los nombres de los ficheros del √ļltimo bundle generado. webpack-bundle-tracker genera toda la informaci√≥n que necesita django-webpack-loader en un fichero llamado webpack-stats.json.

Antes de empezar debemos tener en cuenta ciertos aspectos sobre WebPack:

  • Para generar el √°rbol de dependencias de la aplicaci√≥n Webpack necesita un punto de partida, un fichero JS que leer√° para construir el √°rbol de dependencias, a este JS se le llama entry point.
  • Mediante la definici√≥n de varios entry points lograremos partir nuestro c√≥digo en partes mas peque√Īas, seg√ļn la parte de la web que estemos visitando cargaremos unos bundles u otros haciendo as√≠ la navegaci√≥n mas eficiente.
  • Los loaders son extensiones de terceros que ayudan a Webpack a tratar con varios tipos de ficheros como CSS, im√°genes y ficheros de texto entre otros, el objetivo de los loaders es transformar los ficheros en m√≥dulos, una vez transformados Webpack ya puede emplearlos como dependencias del proyecto.
  • Webpack tambi√©n puede utilizar lo que llama Plugins, estos son extensiones que modifican el comportamiento de Webpack por ejemplo para que haga unas acciones u otras seg√ļn el entorno prod/dev.
  • Existen dos modos de operaci√≥n, desarrollo y producci√≥n, la diferencia principal entre ambos modos es que en producci√≥n se aplica la minificaci√≥n y otras optimizaciones del c√≥digo JS.

Yarn es un nuevo administrador de paquetes JavaScript, es compatible con NPM pero con la ventaja de que opera m√°s r√°pido, de forma m√°s segura y m√°s confiable.

Instalamos NPM(root):

pkg install npm

Instalamos yarn de forma global(root):

npm install -g yarn

Los administradores de paquetes JS precisan de un fichero de configuración package.json para funcionar este indica los comandos disponibles, versiones y demás información asociada a los paquetes, lo generamos mediante el siguiente comando:

cd /home/kr0m/rxWod/rxWodProject
yarn init -y

yarn init v1.22.10  
warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications.  
success Saved package.json  
Done in 0.02s.

El par√°metro -y indica que deseamos generar el fichero package.json con los par√°metros por defecto.

Nos habr√° creado el fichero con el nombre de proyecto rxWodProject, si utilizamos VSCode como editor de c√≥digo, esto emitir√° un warning, para evitarlo lo cambiamos a min√ļsculas:

vi package.json

  "name": "rxwodproject",

Como ya hemos comentado webpack-bundle-tracker es el  plugin de WebPack encargado de generar el fichero webpack-stats.json donde se indica toda la información necesaria para que el paquete django-webpack-loader pueda hacer las sustituciones necesarias en los templates de Django por los nombres de los ficheros de los bundles.

Instalamos WebPack, webpack-cli, webpack-bundle-tracker y progress-bar-webpack-plugin:

cd ..
yarn add webpack webpack-cli webpack-bundle-tracker progress-bar-webpack-plugin

La √ļltima versi√≥n de webpack-bundle-tracker genera el fichero webpack-stats.json con una sintaxis que django-webpack-loader es incapaz de entender, al intentar cargar los datos muestra el siguiente error:

TypeError: string indices must be integers

Afortunadamente django-webpack-loader es extremadamente flexible y permite la carga de datos de diferentes maneras y fuentes tan solo debemos extender la clase webpack_loader.loader.WebpackLoader, esto nos permite cargar las stats desde un fichero de texto, una base de datos o cualquier otra fuente de datos, en mi caso tan solo modificamos ligeramente la clase para que cargue los datos con la nueva sintaxis del fichero webpack-stats.json.

mkdir fixes
vi fixes/webpack.py

from webpack_loader.loader import WebpackLoader

class CustomWebpackLoader(WebpackLoader):
    def filter_chunks(self, chunks):
        chunks = [chunk if isinstance(chunk, dict) else {'name': chunk} for chunk in chunks]
        return super().filter_chunks(chunks)

NOTA: Hay foros en los que recomiendan downgradear la versión de webpack-bundle-tracker pero entonces obtendremos mensajes sobre deprecations:

(node:44658) [DEP_WEBPACK_DEPRECATION_ARRAY_TO_SET] DeprecationWarning: Compilation.chunks was changed from Array to Set (using Array method ‘map’ is deprecated)

Pienso que la mejor opción es este parche hasta que los desarrolladores fixeen el bug.

Creamos el directorio assets donde residirá nuestro código fuente JS y los ficheros generados por WebPack:

mkdir -p assets/js
vi assets/js/index.js

alert("Testing WebPack and Django from AlfaExploit!");

Generamos una configuraci√≥n de WebPack b√°sica donde empleamos el plugin webpack-bundle-tracker y le indicamos un √ļnico fichero entry:

vi webpack.ENV.config.js

var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var ProgressBarPlugin = require('progress-bar-webpack-plugin');

module.exports = {
    mode: "production",
    context: __dirname,
    entry: {
        index: './assets/js/index.js',
    },

    output: {
        path: path.resolve('./assets/bundles/'),
        publicPath: '/static/',
        filename: "[name]-[fullhash].js",
    },

    plugins: [
        new BundleTracker({filename: '../../webpack-stats.json'}),
        new ProgressBarPlugin(),
    ],
}

NOTA: La misma configuraci√≥n sirve para dev pero debemos definir mode: “development”.

Nuestro proyecto tendr√° la siguiente estructura de ficheros/directorios:

|-- assets  
|-- db.sqlite3  
|-- fixes  
|-- manage.py  
|-- node_modules  
|-- package.json  
|-- requirements.txt  
|-- rxWod  
|-- rxWodProject  
|-- usersAuth  
|-- webpack.dev.config.js  
|-- webpack.prod.config.js  
`-- yarn.lock

Django gestiona los assets mediante su configuraci√≥n en el fichero settings.py, primero importamos la librer√≠a os ya que nos har√° falta y a√Īadimos webpack_loader como app de Django.

vi rxWodProject/settings.py

import os
...

INSTALLED_APPS = [
    ...
    'webpack_loader',
]

Luego configuramos los par√°metros relacionados con WebPack:

  • STATIC_URL: Url desde la que colgar√° todo nuestro contenido est√°tico, por ejemplo si queremos acceder a una imagen accederemos a http://DOMAIN_NAME/STATIC_URL/file.png
  • STATICFILES_DIRS: Directorio donde se encuentra el contenido est√°tico generado por WebPack, cuando se ejecute la orden collectstatic se leer√°n los ficheros de este directorio y se copiar√°n al path configurado en STATIC_ROOT.
  • STATIC_ROOT: Directorio donde se copiar√° todo el contenido est√°tico del proyecto cuando se ejecute la orden collectstatic, este contenido estar√° disponible bajo la Url: http://DOMAIN_NAME/STATIC_URL/,¬†este paso solo es necesario cuando pasemos nuestro proyecto a producci√≥n y utilicemos alg√ļn servidor web como Apache/Nginx, ahora por ahora tan solo lo dejamos configurado.
  • WEBPACK_LOADER -> BUNDLE_DIR_NAME : Directorio donde encontrar los bundles de WebPack, como STATICFILES_DIRS ya apunta directamente a ese directorio debemos dejarlo vac√≠o.
    NOTA: If your webpack config outputs the bundles at the root of your staticfiles dir, then BUNDLE_DIR_NAME should be an empty string ‘’, not ‘/’.
  • WEBPACK_LOADER -> LOADER_CLASS: Cargamos nuestra clase parcheada, fixes.webpack.CustomWebpackLoader.
  • WEBPACK_LOADER -> STATS_FILE: Indicamos el nombre del fichero que nos generar√° el m√≥dulo webpack-bundle-tracker.

NOTA: Con el servidor integrado con Django no es necesario ejecutar el comando collectstatic ya que servir√° los bundles directamente desde el directorio STATICFILES_DIRS.

vi rxWodProject/settings.py

STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'assets/bundles/'),
)

STATIC_ROOT = '/usr/local/www/rxWod/static/'

WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': '',
        'LOADER_CLASS': 'fixes.webpack.CustomWebpackLoader',
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
    }
}

NOTA: A fecha de 6/08/2021 ya se ha parcheado el bug de webpack-bundle-tracker por lo tanto los ficheros de configuración quedarían del siguiente modo:

vi webpack.ENV.config.js
var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var ProgressBarPlugin = require('progress-bar-webpack-plugin');

module.exports = {
    mode: "production",
    context: __dirname,
    entry: {
        index: './assets/js/index.js',
    },

    output: {
        path: path.resolve('./assets/bundles/'),
        publicPath: '/static/',
        filename: "[name]-[fullhash].js",
    },

    plugins: [
        new BundleTracker({filename: 'webpack-stats.json'}),
        new ProgressBarPlugin(),
    ],
}

Ha cambiado el path del BundleTracker -> filename.

vi rxWodProject/settings.py
STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'assets/bundles/'),
)

STATIC_ROOT = '/usr/local/www/rxWod/static/'

WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': '',
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
    }
}

En la configuración de WEBPACK_LOADER ya no es necesario indicar un LOADER_CLASS.

Adem√°s cabe remarcar que ya no es necesario el parche fixes/webpack.py, por lo tanto eliminamos el directorio:

rm -rf fixes


Definimos los comandos para compilar los bundles de WebPack, primero eliminamos ficheros generados por compilaciones anteriores y luego lanzamos una compilación nueva, en el caso de dev además lanzamos el servidor integrado de Django:

vi package.json

{
  "name": "rxwodproject",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "webpack": "^5.24.4",
    "webpack-bundle-tracker": "^1.0.0-alpha.1",
    "webpack-cli": "^4.5.0"
  },
  "scripts": {
    "prod-build": "rm -rf assets/bundles/*; yarn webpack --config ./webpack.prod.config.js",
    "dev-build": "rm -rf assets/bundles/*; yarn webpack --config ./webpack.dev.config.js; python manage.py runserver",
    "watch": "rm -rf assets/bundles/*; yarn webpack --watch --config ./webpack.dev.config.js"
  }
}

Instalamos el plugin django-webpack-loader y congelamos las dependencias:

pip install django-webpack-loader
pip freeze > requirements.txt

Compilamos los bundles con WebPack:

yarn dev-build

Podemos dejar WebPack en modo watch para que esté recompilando continuamente los bundles en cuanto se detecten cambios en los ficheros entry o sus dependencias, pero si cambiamos la configuración de WebPack debemos relanzar el comando con el watch.

yarn watch

Nos habr√° generado un fichero:

assets/bundles/index-[hash].js
ls -la assets/bundles/
total 6  
drwxr-xr-x  2 kr0m  kr0m     3 Mar 10 22:27 .  
drwxr-xr-x  4 kr0m  kr0m     4 Mar 10 22:08 ..  
-rw-r--r--  1 kr0m  kr0m  1305 Mar 10 22:27 index-b68ca7fe24923105061f.js

WebPack ya está funcionando ahora queda utilizar los bundles generados en los templates de Django, para ello vamos a emplear django-webpack-loader , este paquete leerá la información del fichero webpack-stats.json para cargar los ficheros de los bundles desde el template de Django.

Si visualizamos el contenido del fichero webpack-stats.json podremos ver la correspondencia entre el nombre del entry point y el bundle generado.

vi webpack.ENV.config.js

    entry: {
        index: './assets/js/index.js',
    },
vi webpack-stats.json
{"status":"done","chunks":{"index":["index-b68ca7fe24923105061f.js"]},"publicPath":"/static/","assets":{"index-b68ca7fe24923105061f.js":{"name":"index-b68ca7fe24923105061f.js","publicPath":"/static/index-b68ca7fe24923105061f.js"}}}

Cuando hagamos referencia a index en nuestro template estaremos cargando el fichero index-b68ca7fe24923105061f.js

Modificamos nuestro template para que cargue nuestro código JS:

vi rxWod/templates/rxWod/index.html

{% extends 'rxWod/base.html' %}
{% load render_bundle from webpack_loader %}

{% block base_body %}
        {% if routines %}
            <ul>
            {% for routine in routines %}
                <li><a>{{ routine.date }}</a></li>
            {% endfor %}
            </ul>
        {% else %}
            <p>No routines available.</p>
        {% endif %}

    {% render_bundle 'index' %}
{% endblock %}

Arrancamos el servidor:

python manage.py runserver

Podemos ver el template y el mensaje emitido vía JS:

Django autom√°ticamente utilizar√° el √ļltimo bundle generado, para comprobarlo podemos hacer un cambio en el c√≥digo JS:

vi assets/js/index.js

alert("Second test: Testing WebPack and Django from AlfaExploit!");

Recompilamos con los bundles:

yarn dev-build

Comprobamos que se ha generado la versión nueva del fichero:

ls -la assets/bundles/

total 6  
drwxr-xr-x  2 kr0m  kr0m     3 Mar 10 22:27 .  
drwxr-xr-x  4 kr0m  kr0m     4 Mar 10 22:08 ..  
-rw-r--r--  1 kr0m  kr0m  1316 Mar 10 22:55 index-b4463486d2c11cf7787f.js

Arrancamos el servidor:

python manage.py runserver

Accedemos a la web y comprobamos que ha actualizado correctamente el mensaje sin tener que tocar el template:

Para poder utilizar CSS en los templates de Django debemos importarlos dentro de nuestro código JS pero para poder hacer esto tendremos que utilizar los loaders de WebPack: css-loader y style-loader .

El primero permite al JS cargar el contenido del CSS y el segundo permite mostrarlo en el DOM del navegador.

yarn add css-loader style-loader

Editamos el fichero webpack.ENV.config.js y a√Īadimos la secci√≥n module, donde indicamos que cuando encuentre un fichero CSS debe utilizar los dos¬† loaders:

vi webpack.dev.config.js

var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var ProgressBarPlugin = require('progress-bar-webpack-plugin');

module.exports = {
    mode: "development",
    context: __dirname,
    entry: {
        index: './assets/js/index.js',
    },

    output: {
        path: path.resolve('./assets/bundles/'),
        publicPath: '/static/',
        filename: "[name]-[fullhash].js",
    },

    plugins: [
        new BundleTracker({filename: '../../webpack-stats.json'}),
        new ProgressBarPlugin(),
    ],

    module:{
        rules:[
            {
                test: /\.css$/i,
                use:['style-loader','css-loader']
            }
       ]
    },
}

Generamos el fichero CSS con el siguiente contenido:

mkdir -p assets/css
vi assets/css/index.css

body {
    background-color: lightblue;
}

Importamos el CSS desde el código JS:

vi assets/js/index.js

import '../css/index.css';
alert("Third test: Testing WebPack and Django from AlfaExploit!");

Recordemos que nuestro template tiene el siguiente aspecto:

vi rxWod/templates/rxWod/index.html

{% extends 'rxWod/base.html' %}
{% load render_bundle from webpack_loader %}

{% block base_body %}
        {% if routines %}
            <ul>
            {% for routine in routines %}
                <li><a>{{ routine.date }}</a></li>
            {% endfor %}
            </ul>
        {% else %}
            <p>No routines available.</p>
        {% endif %}

    {% render_bundle 'index' %}
{% endblock %}

Recompilamos mediante WebPack:

yarn dev-build

Arrancamos el servidor:

python manage.py runserver

Accedemos a la web y comprobamos que hemos cargado el código JS y nuestro CSS:


Adem√°s si miramos los ficheros descargados desde la web podemos ver un √ļnico JS, el resto de ficheros forman parte de la debug toolbar de Django as√≠ que podemos ignorarlos:

Podemos cargar solo la parte JS/CSS indic√°ndolo como segundo par√°metro en el comando render_bundle del template, pero para ello tendremos que generar el fichero CSS mediante el plugin mini-css-extract-plugin .

yarn add mini-css-extract-plugin
vi webpack.ENV.config.js

var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var ProgressBarPlugin = require('progress-bar-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: "development",
    context: __dirname,
    entry: {
        index: './assets/js/index.js',
    },

    output: {
        path: path.resolve('./assets/bundles/'),
        publicPath: '/static/',
        filename: "[name]-[fullhash].js",
    },

    plugins: [
        new BundleTracker({filename: '../../webpack-stats.json'}),
        new ProgressBarPlugin(),
        new MiniCssExtractPlugin({
            filename: '[name]-[hash].css',
        }),
    ],

    module:{
        rules:[
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader'],
            }
       ]
    },
}

NOTA: Al utilizar MiniCssExtractPlugin ya no ser√° necesario el loader style-loader

Recompilamos mediante WebPack:

yarn dev-build

Ha generado el fichero CSS:

ls -la assets/bundles/

total 6  
drwxr-xr-x  2 kr0m  kr0m     4 Mar 10 22:27 .  
drwxr-xr-x  5 kr0m  kr0m     5 Mar 10 22:02 ..  
-rw-r--r--  1 kr0m  kr0m    43 Mar 10 23:27 index-94788942e470c8727cee.css  
-rw-r--r--  1 kr0m  kr0m  3409 Mar 10 23:27 index-94788942e470c8727cee.js

Hagamos la prueba:

vi rxWod/templates/rxWod/index.html

{% extends 'rxWod/base.html' %}
{% load render_bundle from webpack_loader %}

{% block base_body %}
        {% if routines %}
            <ul>
            {% for routine in routines %}
                <li><a>{{ routine.date }}</a></li>
            {% endfor %}
            </ul>
        {% else %}
            <p>No routines available.</p>
        {% endif %}

    {% render_bundle 'index' 'css' %}
{% endblock %}

Como vemos solo ha cargado el CSS, no ha aparecido la ventana de alert del código JS y tan solo se ha bajado el fichero CSS:

Hagamos que solo cargue el código JS:

vi rxWod/templates/rxWod/index.html

{% extends 'rxWod/base.html' %}
{% load render_bundle from webpack_loader %}

{% block base_body %}
        {% if routines %}
            <ul>
            {% for routine in routines %}
                <li><a>{{ routine.date }}</a></li>
            {% endfor %}
            </ul>
        {% else %}
            <p>No routines available.</p>
        {% endif %}

    {% render_bundle 'index' 'js' %}
{% endblock %}

No hace falta recompilar ya que no hemos cambiado nada del JS/CSS.

Accedemos a la web y como vemos solo ha cargado el JS, sin aplicar el CSS:

Podemos ver que se ha bajado el fichero JS:

Si dejamos el extractor de CSS habilitado y no indicamos nada en el comando render_bundle, se cargará tanto el código JS como el contenido CSS pero se bajará dos ficheros y no uno solo como ocurría antes de habilitar el extractor:

vi rxWod/templates/rxWod/index.html

{% extends 'rxWod/base.html' %}
{% load render_bundle from webpack_loader %}

{% block base_body %}
        {% if routines %}
            <ul>
            {% for routine in routines %}
                <li><a>{{ routine.date }}</a></li>
            {% endfor %}
            </ul>
        {% else %}
            <p>No routines available.</p>
        {% endif %}

    {% render_bundle 'index' %}
{% endblock %}

Normalmente el CSS se suele cargar al principio de la sección head:

<head>
    {% render_bundle 'main' 'css' %}

Y el código JS al final del body:

    {% render_bundle 'main' 'js' %}
</body>

Si necesitamos utilizar alg√ļn asset como una imagen podemos utilizar el loader file-loader y el tag webpack_static en los templates.

Instalamos el loader:

yarn add file-loader

Pero hay un problema y es que webpack_static debe estar bugeado y no carga los ficheros de im√°genes hasheados, siempre hace referencia al nombre original sin la parte del hash, la √ļnica soluci√≥n que yo he encontrado es no hashearlos indic√°ndole al file-loader que guarde los ficheros con el nombre original name: ‘[name].[ext]':

vi webpack.ENV.config.js

var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var ProgressBarPlugin = require('progress-bar-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: "development",
    context: __dirname,
    entry: {
        index: './assets/js/index.js',
    },

    output: {
        path: path.resolve('./assets/bundles/'),
        publicPath: '/static/',
        filename: "[name]-[fullhash].js",
    },

    plugins: [
        new BundleTracker({filename: '../../webpack-stats.json'}),
        new ProgressBarPlugin(),
        new MiniCssExtractPlugin({
            filename: '[name]-[hash].css',
        }),
    ],

    module:{
        rules:[
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader'],
            },
            {
                test: /\.(png|jpe?g|gif)$/i,
                loader: 'file-loader',
                options: {
                  name: '[name].[ext]',
                },
            },
       ]
       
    },
}

Creamos el directorio donde almacenaremos las im√°genes y nos bajamos una imagen de ejemplo:

mkdir -p assets/images
fetch https://static.djangoproject.com/img/logos/django-logo-positive.png -o assets/images/django-logo-positive.png

Importamos la imagen desde nuestro JS:

vi assets/js/index.js

import '../css/index.css';
import img from '../images/django-logo-positive.png';

alert("Third test: Testing WebPack and Django from AlfaExploit!");

Compilamos mediante WebPack:

yarn dev-build

Nuestro template quedar√° del siguiente modo:

vi rxWod/templates/rxWod/index.html

{% extends 'rxWod/base.html' %}
{% load render_bundle from webpack_loader %}
{% load webpack_static from webpack_loader %}

{% block base_body %}
        {% if routines %}
            <ul>
            {% for routine in routines %}
                <li><a>{{ routine.date }}</a></li>
            {% endfor %}
            </ul>
        {% else %}
            <p>No routines available.</p>
        {% endif %}

    {% render_bundle 'index' %}
    <img src="{% webpack_static 'django-logo-positive.png' %}"/>
{% endblock %}

Podemos ver como nos muestra la imagen:

Podemos ver los elementos cargados:

El √ļnico inconveniente es que al no generar el fichero hasheado, perdemos la posibilidad de descartar el cacheo de im√°genes del navegador pero para los JS/CSS seguir√° funcionando.

Si te ha gustado el artículo puedes invitarme a un RedBull aquí