Integrar Webpack 4 en un proyecto Django 3

1oLDVI S85Zs 59MJvcg6wA
webpack.base.config.js

En este post explicaré los pasos a seguir para integrar Webpack 4 en cualquier proyecto Django 3 con la ayuda de django-webpack-loader y webpack-bundle-tracker

¿Qué es y por qué necesitas Webpack?

Webpack es básicamente un empaquetador de módulos o module bundler que se encarga de generar gráficos de dependencias con todos ellos y crear assets estáticos a partir de los distintos módulos JavaScript o CSS.

En lugar de tener un montón librerías JavaScript (Bootstrap, jQuery, Colorpicker,…) y llamar los ficheros desde el código HTML, podemos obtener un único fichero bundle.js que los incluye todos. Lo mismo ocurre con los CSS.

No solo eso, Webpack es más que una simple utilidad. También tiene loaders y plugins que extienden sus funcionalidades, y permite automatizar tareas con una configuración bastante fácil y ordenada.

Webpack te será útil si desarrollas una aplicación mayormente JavaScript y con unos cuantos módulos. Si estás preparando una página o aplicación muy pequeña y que apenas tenga 2 o 3 componentes, quizá sea demasiado grande para ti. No hay que fliparse y utilizar todo tipo de tecnología moderna para estar a la última. Tenemos que ser ágiles y realistas.

Los módulos que ayudan a integrar Webpack en Django

Webpack se puede utilizar en prácticamente cualquier tipo de proyecto. En el fondo no necesitamos nada especial para que funcione con Django, pero sí existen dos componentes que ayudan en algunas cosas.

En primer lugar, podemos usar webpack-bundle-tracker, que se encarga de “emitir” información sobre el estado de la compilación de Webpack a un fichero JSON que será interpretado por django-webpack-loader, y este utilizará la información generada para integrarlo con Django.

Paso 1: Instalar webpack

El primer paso es generar un package.json y instalar los paquetes necesarios:

npm init

En el fichero package.json podemos añadir los siguientes scripts. Al final me decanté por crear dos webpack.config.js distintos, uno para desarrollo y otro para producción, así que quedaría de esta forma:

...
"main": "webpack.config.js",
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack --watch --mode development",
    "start": "webpack-dev-server --open",
    "build": "webpack --config webpack.base.config.js --progress --colors",
    "build-production": "webpack --config webpack.production.config.js --progress --colors"
  }
...

Esto nos abre la posibilidad de ejecutar los siguientes comandos:

npm run test
npm run watch
npm run start
npm run build
npm run build-production

Paso 2: Crear el fichero webpack.config.js

Como comentaba, en mi caso creé uno para cada entorno.

webpack.base.config.js
webpack.production.config.js

Os voy a compartir el que creé para uno de mis proyectos. He eliminado algunas partes porque tiene varios ficheros css y js añadidos y he dejado dos o tres en cada bundle.

Añadí algunos extras como por ejemplo:

  • IgnorePlugin: Para ignorar ciertos ficheros.
  • ProvidePlugin: Para cargar módulos en lugar de importarlos.
  • ContextReplacementPlugin: Para reemplazar con expresiones regulares. Lo utilicé porque tenía problemas con los .js de la carpeta /locales de uno de los módulos, y con este plugin puedo substituirla con un módulo vacío.
  • En el output: path le indicamos la carpeta donde debe generar los bundles. En este caso: /assets/bundles/

Paso 3: server.js

Necesitamos un servidor node para que Webpack genere los bundles. Para ello necesitamos el fichero server.js, este es el mio:

Paso 4: Instalar django-webpack-loader

Basta con instalar el módulo de Django con pip

pip install django-webpack-loader

Y añadirlo en el fichero requirements.txt

django-webpack-loader==0.7.0

En mi caso he configurado el settings.py para añadir lo siguiente. Primero la carpeta /assets/, que es donde se generan los bundles, en la variable STATICFILES_DIRS

STATICFILES_DIRS = [
...
os.path.join(BASE_DIR, 'assets')
]

Luego la variable WEBPACK_LOADER indicando la carpeta “bundles/” donde se generan (recordamos que es: /assets/bundles), y en el STATS_FILE debemos indicarle el fichero generado por webpack-bundle-tracker, que en mi caso es el de desarrollo.

# Webpack
WEBPACK_LOADER = {
"DEFAULT": {
"CACHE": not DEBUG,
"BUNDLE_DIR_NAME": "bundles/",
"STATS_FILE": os.path.join(BASE_DIR, "webpack-stats.base.json"),
"POLL_INTERVAL": 0.1,
"TIMEOUT": None,
"IGNORE": [".*\.hot-update.js", ".+\.map"],
'LOADER_CLASS': 'webpack_loader.loader.WebpackLoader',
}
}

Paso 5: Indicar los bundles en las plantillas de Django

Nos queda indicarle a Django cómo cargar los bundles generados. En mi caso decidí crear los bundles vendor y main tanto para los ficheros CSS como los JS.

En primer lugar necesitamos cargar el render_bundle del módulo webpack_loader (línea 19)

Ahora ya podemos utilizarlos en la plantilla de Django de la siguiente forma:

<!-- Webpack bundles -->
{% render_bundle "vendor" "css" %}
{% render_bundle "main" "css" %}
....
<!-- Webpack bundles -->
{% render_bundle "vendor" "js" %}
{% render_bundle "main" "js" %}

Y de esta forma, podemos cargar los CSS dentro del <head> y los JS antes de cerrar el <body>

Ahora ya podemos utilizar collectstatic para copiar los ficheros generados en nuestra carpeta de estáticos en producción.

python manage.py collectstatic

Con esto debería ser suficiente para tener todo en funcionamiento. Si se os ocurre alguna forma mejor, será bienvenida.

Como implementar django-registration y personalizar sus plantillas

Quienes me conocen en el ámbito laboral, saben lo mucho que me gusta Django 😄

Hace años que muchos de los proyectos personales relacionados con aplicaciones web, los hago acompañados de este framework Python.

En este artículo daré 4 pistas sobre cómo implementar django-registration y poder personalizar las plantillas visuales y las que utiliza para enviar los correos de registro y validación.

El objetivo es tener una aplicación con un sistema de registro de usuarios que tenga todas las funcionalidades básicas cubiertas, y además, personalizadas. Todo está probado con Django 3.0.3 y django-registration 3.1.

No es un artículo que entre en detalle y en el que estén todas las líneas de código necesarias para que esto funcione. Es un pequeño resumen que creo que puede servir para sacar dudas que yo mismo me encontré a lo largo del desarrollo.

Vamos a ver los siguientes pasos.

  • Instalar y configurar django-registration
  • Registro de uno o de dos pasos?
  • Personalizar todas las plantillas y correos
  • Ejemplos de plantillas personalizadas

Instalar y configurar django-registration

No voy a detallar los pasos más fáciles de la instalación porque están mejor explicados en la documentación oficial. En lugar de esto, explicaré qué configuración he preparado yo para uno de mis proyectos, que en más de un caso ha sido fruto de un buen rato de I+D hasta conseguir el funcionamiento que quería.

Tras la instalación via requirements.txt o pip, el siguiente paso es configurarlo a través del fichero settings.py

# Django registration
ACCOUNT_ACTIVATION_DAYS = 7
REGISTRATION_OPEN = True
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = os.path.join(BASE_DIR, 'tmp/email')

En mi caso he configurado 7 días de activación (hasta que caduca el enlace). Las otras 3 líneas las he utilizado en el entorno de desarrollo y personalizado en el fichero settings.py de producción. En este caso me interesa poder abrir o cerrar el registro a nuevos usuarios, y en el entorno de desarrollo desactivar el envío de mails, que en realidad se guardan dentro de un fichero en la carpeta /tmp/email y es mucho más fácil consultarlos de esta forma que montar un servidor de correo interno.

Después hay que configurar las URL’s para decirle a Django qué módulos cargar y bajo qué patrones.

from django.urls import include, path

urlpatterns = [
    ...
    path('accounts/', include('django_registration.backends.activation.urls')),
    path('accounts/', include('django.contrib.auth.urls')),
]

Ya que más adelante explicaré cómo personalizar todas las plantillas y URL’s, hay que tener en cuenta que en caso de querer hacerlo, las líneas anteriores deben ir al final de las que personalicemos.

Registro de uno o de dos pasos?

Hay dos formas de implementar el registro. Con el de un solo paso conseguimos que el usuario esté confirmado y sin necesidad de validar su cuenta de correo.

En cambio, el registro de dos pasos obliga al usuario a validar su dirección de correo. Hay pocas excusas para no usar este segundo método.

La diferencia en la configuración es que, si queréis utilizar el registro de un solo paso, en lugar de importar las URL de backend.activation.urls hay que importar:

path('accounts/', include('django_registration.backends.one_step.urls')),

Personalizar todas las plantillas y correos

Aquí viene lo divertido. En mi caso, que siempre me gusta tenerlo todo bien ordenado en una estructura de carpetas y ficheros lo más coherente posible, he personalizado la localización de todas las plantillas visuales y las que se utilizan para en envío de correos.

1 bf7tPArrqnPog0PEFdCvIA 1

Tengo una aplicación de Django llamada users que centraliza todo lo relacionado con los usuarios de la web.

Dentro de la carpeta /templates/registration tengo todas las relacionadas con el proceso de registro. Plantillas de la aplicación y de los correos. La de login.html dentro de templates pero fuera de registration.

Para que esto funcione, debemos personalizar las URL’s para que carguen las plantillas en estas rutas, que no son las que trae por defecto django-registration.

Registro y correo de activación

Este primer path es para crear la página de registro bajo la URL accounts/register. Aquí cargamos la plantilla registration_form.html y llamamos al RegistrationView pasándole también la ruta de las dos plantillas de correo que se utilizarán para generar el mail de activación.

1 mnNN CsCPnhN7fsDK074mg
path('accounts/register/',
RegistrationView.as_view(
form_class=CustomUserForm,
template_name="registration/registration_form.html",
email_body_template = 'registration/activation_email_body.txt',
email_subject_template = 'registration/activation_email_subject.txt'
),
name='django_registration_register',
),

En el fichero forms.py declaramos la clase CustomUserForm de la siguiente manera:

from django_registration.forms import RegistrationForm
class CustomUserForm(RegistrationForm):
username = forms.Field(
widget=forms.TextInput(
attrs={'placeholder': _('Username')}
)
)
email = forms.Field(
widget=forms.EmailInput(
attrs={'placeholder': _('Email')}
)
)
password1 = forms.Field(
widget=forms.PasswordInput(
attrs={'placeholder': _('Password')}
)
)
password2 = forms.Field(
widget=forms.PasswordInput(
attrs={'placeholder': _('Type your password again')}
)
)
class Meta(RegistrationForm.Meta):
model = get_user_model()
fields = ('username', 'email')

Registro cerrado

La que muestra el mensaje de error a los usuarios que intentan registrarse cuando tenemos el parámetro REGISTRATION_OPEN = False

path('accounts/register/closed/',
TemplateView.as_view(template_name='registration/registration_closed.html'),
name='registration_disallowed'
),

Registro completado

La plantilla que carga una vez el usuario finaliza el proceso de registro, pero todavía no ha activado la cuenta. Aquí podemos indicarle que revise su bandeja de correo para hacer click en el enlace de activación.

path('accounts/register/complete/',
RegistrationView.as_view(
form_class=CustomUserForm,
template_name="registration/registration_complete.html"
),
name="registration_complete",
),

Registro verificado

A diferencia de la anterior, esta es la página que verá el usuario tras hacer click en el enlace de validación.

path('accounts/activate/complete/',
RegistrationView.as_view(
template_name='registration/activation_complete.html'
),
name='django_registration_activation_complete'
),

Reset de contraseña

Que no se os olvide añadir la opción para que los usuarios puedan resetear su contraseña.

De esta forma, llamando al PasswordResetView, podemos personalizar el nombre de la plantilla en el template_name. Y además, necesitaremos personalizar la pantalla de resultado:

path('accounts/password/reset/',
auth_views.PasswordResetView.as_view(
template_name='registration/password_reset.html'
),
name='password_reset'
),
path('accounts/password_reset/done/',
auth_views.PasswordResetView.as_view(
template_name='registration/password_reset_done.html'
),
name='password_reset_done'
),

Fallo de validación

Los enlaces de activación tienen un formato concreto. Se genera una clave que se utiliza en la URL para que Django pueda verificar la acción. En caso de fallo, también podemos personalizar la plantilla con el mensaje que verá el usuario:

url(r'^accounts/activate/(?P<activation_key>[-:\w]+)/$',
    ActivationView.as_view(
        template_name = 'registration/activation_failed.html'
    ),
    name='django_registration_activate'
),

Ejemplos de plantillas personalizadas

Para terminar, os adjunto un par de plantillas de ejemplo personalizadas que he utilizado en uno de mis proyectos. Concretamente, la aplicación web StoryDevil

Así configuro el móvil para evitar distracciones y priorizar lo importante

0*mLtsmjiqj4kkdwVV
Photo by mnm.all on Unsplash

Quiero explicar cómo tengo configurado el móvil (un iPhone) para gestionar bien las distracciones del día a día y priorizar aquellas notificaciones, llamadas y demás, únicamente para las personas y el contenido que sean realmente importantes para mí. Estos cacharros nos molestan más de lo que deberían y nos hacen perder un tiempo muy valioso.

Siempre en silencio (salvo honradas excepciones)

Mi iPhone, por defecto, siempre está en silencio. Raramente escucharás uno de mis tonos, pero configuro excepciones (que explico más adelante). Nadie quiere escuchar sonidos de teléfonos ajenos. Gracias al Apple Watch me entero de mis notificaciones. Nadie más tiene porqué hacerlo.

WhatsApp

Esta aplicación nos “controla”, y no solo recogiendo y analizando todos los datos posibles (localización, contactos, mensajes, horas activas, etc) sino que también logra que nuestros amigos controlen nuestro tiempo.

Y tu tiempo es tuyo.

No estoy en grupos molestos ni llenos de gilipolleces. Si alguna vez me han añadido a alguno, salgo sin dar explicaciones. En los que estoy, todos están silenciados excepto dos, y ambos tienen solo 3 miembros.

Los contactos también, la mayoría en silencio. Solo 5 personas (y dos grupos) no lo están, y es porque cuando me hablan, quiero enterarme al momento. Al resto, les atenderé cuando pueda y cuando quiera. Si es realmente urgente, nadie manda un WhatsApp.

1

Lista de favoritos y llamadas

Una llamada suele ser importante, y si es de un amigo o familiar todavía más. Todas las llamadas de números que tengo en la agenda, llegan con una vibración constante del Apple Watch y la pantalla iluminada del iPhone.

En estos casos, el teléfono no suena. Con que vibre me es suficiente y no tengo porqué molestar a los demás.

Pero hago una excepción.

Tengo una lista de 3 contactos favoritos VIP. Configurados con la máxima prioridad de contacto para que puedan alcanzarme sea como sea. Si llaman, además de la vibración, y aunque el teléfono está en silencio, este sonará igualmente y al máximo volumen.

2 4

Además, iOS tiene una funcionalidad que le añade un extra a posibles llamadas y mensajes de contactos muy importantes, el Emergency Bypass. Con esta funcionalidad, cuando uno de estos contactos te llama o te manda un iMessage, iOS se salta incluso el modo “No molestar” y hace que el móvil suene y vibre.

3 5

Llamadas de números no reconocidos

Hoy en día es muy común recibir llamadas de números que no tenemos en la agenda y que casi siempre son de empresas de marketing que tratan de vendernos algo. He solucionado este tema de raíz. iOS tiene una opción para silenciar y bloquear todas las llamadas si el número entrante no está en tu propia agenda, y así lo hice.

Tiempo, cabreos, disgustos. Para mí, las llamadas de marketing pueden irse, con perdón, a tomar por el culo. Sean de quien sean. Incluso de la oficina del banco o del seguro que ya tengo contratado. Si es algo realmente interesante para ti, eres tu quien iniciará la llamada, no ellos.

4 7

Un correo electrónico tampoco es urgente

Si alguien te envía un mail, ya sea corporativo o personal, no es para avisarte sobre un tema de vital importancia que necesite ser atendido al instante. De ser así, te llamará.

En mi caso utilizo Outlook y lo tengo configurado para recibir notificaciones únicamente con la actualización del contador visible en el icono de la app. Si desbloqueo el móvil y veo que tengo correos pendientes, los revisaré si quiero dedicarle tiempo en ese momento. De lo contrario, no.

Incluso así, es importante ser ágil respondiendo y gestionando mails. Tampoco es cuestión de pasar de todo, sino simplemente gestionar las tareas eficientemente y a su debido tiempo.

El modo “No Molestar”

Este modo de iOS también es útil para activarlo puntualmente o programarlo a unas determinadas horas del día, como por ejemplo por las noches y/o cuando te vas a la cama.

En mi caso, lo activo siempre por las noches pero permitiendo llamadas de mis favoritos y, además, si alguien que no lo está insiste (Repeated Calls) sonará igual porque es posible que sea un tema urgente.

5 1

Con todo esto he conseguido que el móvil me moleste lo más mínimo y sea yo quien controle mi tiempo, no los demás lo hagan por mí.

Jugando con la Lente Anamórfica de Moment

Hace unos días compré la lente anamórfica de Moment para usarla con mi iPhone XS. Un gran juguete para los amantes del cine y la fotografía. Por poco más de 150 dólares puedes hacer fotos y vídeos con una lente emulando una cámara de cine de forma muy decente.

Nació como un proyecto de Kickstarter y lograron recaudar más de $800,000 de los $50,000 que necesitaban. No somos pocos los que queremos lograr ese look cinematográfico con lo que tenemos en casa. A ese precio es muy tentador, incluso ideal como simple hobby.

Es difícil conseguirla en alguna tienda española, yo la compré directamente en la tienda oficial de Moment, aunque también se puede conseguir a través de Amazon.com

1 1
Foto sin ningún retoque adicional y hecho con la app oficial de Moment. Mi bar de chinos favorito 😀

Básicamente es una lente anamórfica de 1.33x con un aspect ratio de 2.40:1. La lente comprime la imagen, y con la aplicación de grabación de Moment o la de FiLMiC, se “descomprime” para obtener la imagen correcta consiguiendo incluso efectos de Lens Flares a lo JJ. Abrams 🥳

La aplicación oficial de Moment para iOS tiene un precio de $5.99, pero fijaros bien al comprar la lente porque suelen regalar un código de descuento para poder bajarla gratis.

2
3
Los lens flares crearon un efecto curioso con la luz del semáforo.
4

Ponerla y quitarla es bastante fácil. Las carcasas de Moment están adaptadas para cada lente y basta con un simple giro de 90 grados para que quede perfectamente acoplada y alineada con la cámara del móvil.

La construcción de la lente es de muy buena calidad. Incluye una tapa frontal , pero no una trasera que pueda usarse para proteger la entrada de la lente. Para hacerlo y que quede bien protegida hay que guardarla dentro de la caja original, aunque también trae una pequeña bolsa para transportarlo.

5
6
7
8
9