Como integrar Django 3 con Vue 3 y no morir en el intento

Mar 01 Dic 2020 | Por: Roylan Suarez

Hola amigos de CodigoJS, Desarrollo web en Sevilla, hoy os comparto un post súper interesante sobre como integrar dos tecnologías punteras en el desarrollo web para afrontar proyectos de cualquier alcance.

Hasta hace poco la mayoría de los proyectos utilizaban en el frontend JQuery para hacer la web más interactiva. La llegada de frameworks JS como React y VueJS  ha facilitado el desarrollo de la web, permitiéndonos hacer sitios web más robustos y atractivos.

Lo anterior me ha llevado a desarrollar este tutorial que explica cómo integrar Django 3 y VueJS 3 en el siguiente escenario:

Supongamos que el proyecto que vamos a desarrollar es crítico para SEO y que somos fans de Django, queremos cubrir todo lo relacionado con JavaScript utilizando VueJS. Por tanto, utilizaríamos Vue en formularios de reservas que interactúan con el backend, etc...

Esta integración nos va ha permitir escribir los componentes de Vue utilizando todas las facilidades que nos ofrecen los Componentes de un Solo Archivo (Single File Components) y compilarlos para que todos los navegadores los puedan leer sin ningún tipo de problema.

Sin más manos a la obra...

Tabla de contenidos


Crear un Proyecto Django 3

Lo primero que haremos será crear un nuevo proyecto con Django 3. Nos vamos a la consola y escribimos los siguientes comandos para crear el directorio de nuestro proyecto:

$ mkdir django-vue
$ cd django-vue

Entorno virtual Python

El próximo paso es crear un entrono virtual para Python.

$ python3 -m venv venv

Después lo activamos

$ source venv/bin/activate

y por último comenzamos a instalar Django en su última versión

$ pip install django

Crear proyecto Django 3

Ahora vamos a crear un nuevo proyecto con Django 3 con el nombre djvue

$ django-admin startproject djvue

accedemos a la carpeta del proyecto

$ cd djvue

ejecutamos las migraciones de la base de datos

$ python manage.py migrate

de esta forma ya podemos correr nuestro servidor de desarrollo en Django

$ python manage.py runserver

por último nos vamos al navegador y cargamos la siguiente url: http://localhost:8000 donde podremos ver la página inicial del proyecto.

Instalar django-webpack-loader

El siguiente paso, es instalar django-webpack-loader el cual inyecta en nuestras plantillas html de Django los js y css generados por Vue3. Para entendenrlo un poco mejor, esta app actúa como intermediario entre Django y Vue.

$ pip install django-webpack-loader

Configurar el proyecto Django

Ahora vamos a modificar nuestro proyecto para establecer la configuración de django-webpack-loader y los archivos estáticos. Para ello editamos el archivo settings.py de nuestro proyecto y establecemos las siguientes configuraciones.

En el top del archivo importamos el modulo os de python

import os

En la sección del INSTALLED_APPS añadimos 'webpack_loader', para que active la aplicación recién instalada.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'webpack_loader',
]

En la sección de TEMPLATES donde está la clave 'DIRS': [], la actualizamos para que quede de la siguiente forma:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates/')], # Establecemos el directorio de las plantillas
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

A continuación nos vamos a la raíz de nuestro proyecto y creamos el directorio templates

Seguimos ahora con la configuración de los archivos estáticos de Django. Nos vamos al final del archivo y debajo de STATIC_URL añadimos las siguientes opciones.

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'public/static/')

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'public/media/')

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'public/static_dev/'),
    os.path.join(BASE_DIR, "ui/dist"), # Bundle de VUE
)

Aquí lo más importante es destacar el directorio ui/dist en el cual se va a generar el bundle o archivos estáticos del proyecto Vue3 que veremos más adelante.

Ahora vamos a crear la configuración de webpack-loader, el cual como dije anteriormente será el encargado de inyectar los archivos estáticos js y css generados por Vue en nuestras plantillas html.

# Directorio de Vue
UI_DIR = os.path.join(BASE_DIR, 'ui/')

# Opciones de webpack-loader
WEBPACK_LOADER = {
    'DEFAULT': {
        'CACHE': not DEBUG,
        'BUNDLE_DIR_NAME': 'webpack_bundles/',
        'STATS_FILE': os.path.join(UI_DIR, 'webpack-stats.json'),
        'POLL_INTERVAL': 0.1,
        'TIMEOUT': None,
        'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
        'LOADER_CLASS': 'webpack_loader.loader.WebpackLoader',
    }
}

Lo más importante a destacar aquí en la clave STATS_FILE, en la que se guarda el fichero donde se van a mapear los archivos del bundle de Vue3. Este archivo se genera de forma automática así que no tenemos que hacer nada.

Listo, hasta aquí todas las configuraciones de Django. Antes de continuar, vamos a guardar todos los cambios y ejecutar el proyecto para comprobar que todo está bien.

Instalación de Vue3

Lo primero que debemos comprobar es si tenemos node instalado, nos vamos a la consola y ejecutamos el siguiente comando:

$ node -v
v12.20.0

En mi caso tengo la versión 12.20.0. Si no tienes node instalado, vamos a la consola y lo instalamos con el siguiente comando:

$ sudo apt install nodejs

Si quieres instalar la última versión o alguna en específico te recomiendo este tutorial de Digital Ocean.

Una vez comprobado que tenemos node instalado, procedemos a instalar Vue.

Para instalar VueJS 3 vamos a utilizar Vue CLI, que no es nada más que una herramienta de node que nos permite crear fácilmente un nuevo proyecto. Por tanto lo primero que debemos instalar es Vue CLI de forma global, para todo el sistema.

$ npm install -g @vue/cli

Crear proyecto Vue3

El proyecto de Vue los vamos a crear en la raiz del proyecto de Django, con el nombre ui (user interface). Nos vamos a la consola y ejecutamos

$ vue create ui

Vue CLI nos preguntará por la versión de queremos instalar.

Seleccionamos la opción. Default, como se ve en la imagen para instalar Vue 3, presionamos enter y esperamos a que el proceso termine.

Una vez terminado accedemos al directorio de proyecto como sugiere la ayuda del instalador.

 $ cd ui

y para comprobar que todo ha ido bien, ejecutamos el servidor de desarrollo de Vue3

 $ npm run serve

abrimos el navegador y cargamos la url http://localhost:8080/

Llegado a este punto debemos saber, que para la ejecución de este proyecto, debemos tener dos terminales abiertas, una con el proyecto de Django y otra con Vue.

Configurar Vue3

Necesitamos establecer algunas configuraciones antes de continuar. Lo primero es instalar el paquete de node webpack-bundle-tracker, que hará un seguimiento de los archivos estáticos generados por Vue en el archivo webpack-stats.json, el cual añadimos anteriormente en la configuración de Django. Dicho esto lo instalamos desde la terminal.

$ npm install --save-dev webpack-bundle-tracker@0.4.3

Importante: En el momento que se escribe este tutorial, debemos instalar la versión 0.4.3 que es la última estable, si instalamos la última versión, que está en el canal alpha no va a funcionar. Así que tened en cuenta este punto.

Una vez finalizada la instalación del paquete, vamos crear la configuración de Vue, para que nos permita crear el bundle o archivos estáticos js y css para que Django los pueda leer e insertarlos en las plantillas html.

En el directorio ui creamos un archivo llamado vue.config.js  y le añadimos el siguiente contenido:

// Importamos webpack-bundle-tracker
const BundleTracker = require("webpack-bundle-tracker");

module.exports = {
    // La ruta donde estará disponible el bundle de los archivos estáticos 
	publicPath: "http://0.0.0.0:8080/",
    // Directorio donde se creará el bundle de archivos estáticos
    outputDir: './dist/',
    // Estable que se compile en tiempo de ejecución.
	runtimeCompiler: true,

    // Los puntos de entrada de nuestra aplicación.
	pages: {
		main: {
		    // entry for the page
		    entry: 'src/main.js',
        },
			
	},

	chainWebpack: config => {
		config.optimization
		.splitChunks(false)

		config
        .plugin('BundleTracker')
        // El archivo que mapeará los estáticos del proyecto.
		.use(BundleTracker, [{filename: './webpack-stats.json'}])

		config.resolve.alias
		.set('__STATIC__', 'static')

		config.devServer
		.public('http://0.0.0.0:8080')
		.host('0.0.0.0')
		.port(8080)
		.hotOnly(true)
		.watchOptions({poll: 1000})
		.https(false)
		.headers({"Access-Control-Allow-Origin": ["\*"]})
	}
};

Este archivo es sumamente importante, he comentado las opciones más relevantes, si quieres profundizar te recomiendo echarle un ojo a la documentación.

Crear un vista en Django.

Nos vamos a la consola de Django, para crear una app donde incluiremos nuestros componentes de Vue3

$ python manage.py startapp index

Editamos el archivo de configuración settings.py y la añadimos a las INSTALLED_APPS

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'webpack_loader',

    'index.apps.IndexConfig',
]

Editamos el archivo views.py de la app index y creamos una vista tan sencilla como esta.

from django.shortcuts import render

def index(request):
    context = {}

    return render(request, 'index.html', context)

Ahora necesitamos crear la plantilla index.html. En la misma aplicación index, creamos un directorio llamado templates y después otra llamada index, y dentro de esta última el archivo index.html, como se puede ver a continuación:

Editamos la plantilla y añadimos algo de contenido

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Integrar Django 3 y Vue 3</title>
</head>
<body>
    <h1 style="text-align: center;">Como integrar Django 3 y VueJS 3</h1>
</body>
</html>

Ahora toca definir la url de nuestra vista. Primero vamos a crear el archivo urls.py dentro de la app index con el siguiente contenido:

from django.shortcuts import render

def index(request):
    context = {}

    return render(request, 'index/index.html', context)

Seguido definimos nuestra url dentro de la aplicacion index. Creamos un archivo llamos urls.py dentro de la app index con el siguiente contenido:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

y por último incluimos las rutas de la aplicación index en el archivo urls.py principal de nuestro proyecto: djvue -> urls.py con el siguiente contenido:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),

    path('', include('index.urls')),
]

Listo, ahora ya podemos recargar la página y comprobar que se ha cargado nuestra vista.

Inyectar VueJS en las plantillas HTML

Ya estamos llegando al final. Ahora vamos decirle a Django como insertar nuestros componentes de Vue en las plantillas html.

Editamos la plantilla index.html y añadimos el siguiente templatetags.

{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Integrar Django 3 y Vue 3</title>
</head>
<body>
    <h1 style="text-align: center;">Como integrar Django 3 y VueJS 3</h1>

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

Al principio cargamos el template tag render_bundle y al final, antes de cerrar el body insertamos el bundle main, que corresponde al archivo main.js que tenemos declarado en el archivo de vue.config.js, especificamente en esta sección:

...
// Los puntos de entrada de nuestra aplicación.
	pages: {
		main: {
		    // entry for the page
		    entry: 'src/main.js',
        },
			
	},
...

Para comprobar que todo está funcionando bien, recargamos la página y pulsamos ctrl + u para ver código fuente:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Integrar Django 3 y Vue 3</title>
</head>
<body>
    <h1 style="text-align: center;">Como integrar Django 3 y VueJS 3</h1>

    <script type="text/javascript" src="http://0.0.0.0:8080/js/main.js" ></script>
</body>
</html>

Si nos fijamos, antes del body se está cargando el archivo main.js desde el servidor de desarrollo de Vue que debemos tener en ejecución en otra terminal. Si hacemos clic en http://0.0.0.0:8080/js/main.js podemos ver su contenido...

Guay!!!!! estamos a un pelín de lograrlo

Pintando el primer componente

Editamos el archivo src/main.js que es el punto de entrada de nuestra aplicación Vue con el siguiente contenido.

import { createApp, ref } from 'vue'
// Importamos el componente HelloDjango
import HelloDjango from './components/HelloDjango'

// Creamos una instancia de la aplicación Vue
const app = createApp({
  // Elemento html donde se va ha renderizar el contenido
  el: '#app',
  // Cambiamos los delimiters de las variables para que sean diferentes a los de Django
  delimiters: ['[[', ']]'],
  // Activamos el componente dentro de la app
  components : {
    HelloDjango
  },
  // Creamos variable msg reactiva con ref
  data () {
    return {
      msg: ref('Componente de VueJS 3')
    }
  },
})
// Montamos la app en el div #app de nuestra plantilla index.html.
app.mount('#app')

Creamos el componente HelloDjango.vue dentro de la carpeta src/components con el siguiente contenido

<template>
  <div style="color: red;">Este el primer componente de Vue3 insertado en Django 3</div>
</template>

<script>
export default {
  name: 'HelloDjango',
}
</script>

<style scoped>

</style>

Por último editamos nuevamente el archivo index.html de Django e insertamos un div debajo del h1 con el siguiente contenido:

...
<div id="app" style="text-align: center;">
   [[ msg ]]
   <hello-django/>
</div>
...
...

Aquí estamos creando el punto de montaje de nuestra aplicación de Vue al establecer el id="app"

  • [[ msg ]] es la variable que creamos en el data utilizando los delimiters [] para no crear conflictos con Django.
  • <hello-django/> es el componente HelloDjango.vue que creamos en nuestro proyecto Django.

Ya, se acabó, ahora si, recargamos nuevamente la página y deberíamos de ver los siguiente:

Conclusiones

Hemos visto como integrar Django3 y Vue3 para hacer una web amigable para el SEO y totalmente dinámica utilizando las tecnologías actualizadas.

¿Que más podríamos hacer? Pues todo lo que se nos ocurra:

  • Montar una web con Django3, Vue3 y Bulma o Tailwind CSS
  • Integrar Django Rest Framework en el backend y crear componentes totalmente dinámicos con Vue como formulario de reservas, sliders, etc...

En fin con esta combinación de tecnologías podemos desarrollar proyecto de cualquier alcance.

Agradecimientos

Quiero agradecer a todos lo que durante algunos días me ayudaron a resolver algunos problemas que se presentaron:

  • Skirtle en el forum de Vue
  • Daniel, compañero en trabajo en CodigoJS
  • Al grupo de VueJS en español en Telegram
  • Al grupo de Django en español de Telegram

Espero que os sea tan útil como a nosotros y si tenéis alguna duda o sugerencia déjanos un comentario.

 

 

 

 

 



Déjanos un comentario


7 Comentarios

jorge alarcon11 de Enero de 2021 a las 15:09

Como Integrar Django3 and Django Rest Framework en el backend y crear componentes totalmente dinámicos con Vue3, lo que intento hacer es comunicar las dos partes con la API


danilo5 de Marzo de 2021 a las 05:14

La línea
path('', include('index.urls')),
no funciona. He vuelto a hacer el tutorial 3 veces la última copiando y pegando todo y siempre llego a este error:
raise ImproperlyConfigured(msg.format(name=self.urlconf_name)) from e
django.core.exceptions.ImproperlyConfigured: The included URLconf '<module 'indice.urls' from 'D:\\pages_perso\\Websites\\Practices\\python\\django\\django-vue3\\djvue\\indice\\urls.py'>' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused by a circular import.

Es muy lamentable que un tutorial que prometía ser el mejor, que había empezado tan bien tenga semejantes errores y sea publicado sin verificar.

Esta es la forma en que me funcionó
from indice import views as indview

urlpatterns = [
path('admin/', admin.site.urls),

path('', indview.index, name='index'),
]


roylans5 de Marzo de 2021 a las 12:53

Hola danilo.

Gracias por tu comentario. Faltaba incluir el archivo urls.py de la app index.
Esta solucionado ahora.


david24 de Marzo de 2021 a las 04:09

Interesante articulo, solo una duda
como haces cuando pones en producción tu aplicación?
donde indicas que tus componentes vue los tomaras de un repositorio estático?


José Antonio Mingorance18 de Mayo de 2021 a las 16:06

Hola!!!

Lo primero de todo, muchísimas gracias por el tutorial, está super claro y me parece muy útil.

Me gustaría preguntarte por un error que me está saliendo a la hora de seguir el tutorial

básicamente mis problemas empiezan al utilizar {% render_bundle 'main' %} dentro de mi index.html...

line 90, in get_bundle
asset = assets['assets'][chunk]
KeyError: 'assets'

Te pego el error por aquí, estoy mirando en foros pero nada, no lo consigo. Si pudieras echarme un cable te lo agradecería muchísimo!!

Un saludo y gracias de antemano!

ERROR:

Request Method: GET
Request URL: http://127.0.0.1:8003/test-vue/
Django Version: 2.2
Exception Type: KeyError
Exception Value:
'assets'
Exception Location: /opt/anaconda3/envs/app-tidart/lib/python3.6/site-packages/webpack_loader/loader.py in get_bundle, line 90
Python Executable: /opt/anaconda3/envs/app-tidart/bin/python
Python Version: 3.6.9
Python Path:
['/Users/Jose/Documents/biddeo/app-tidart',
'/Users/Jose/Documents/biddeo/app-tidart',
'/Applications/PyCharm.app/Contents/plugins/python/helpers/pycharm_display',
'/opt/anaconda3/envs/app-tidart/lib/python36.zip',
'/opt/anaconda3/envs/app-tidart/lib/python3.6',
'/opt/anaconda3/envs/app-tidart/lib/python3.6/lib-dynload',
'/opt/anaconda3/envs/app-tidart/lib/python3.6/site-packages',
'/opt/anaconda3/envs/app-tidart/lib/python3.6/site-packages/pycrypto-2.6.1-py3.6-macosx-10.7-x86_64.egg',
'/Applications/PyCharm.app/Contents/plugins/python/helpers/pycharm_matplotlib_backend']
Server time: Tue, 18 May 2021 16:01:20 +0000


roylans24 de Mayo de 2021 a las 02:28

Lo que se me ocurre de momento es que verifiques la versión de webpack-bundle-tracker que estas usando, al momento de escribir el post habia que usar la versión webpack-bundle-tracker@0.4.3.

Por otra parte rectifica el proceso... yo lo he probado en varias ocasiones y no me ha dado problemas.

Saludos y cuentanos


Mario oio8 de Julio de 2021 a las 12:53

El error de :
asset = assets['assets'][chunk]
KeyError: 'assets'

viene por la versión de django-wepack-loader.

prueba a bajarle la versión.

pip uninstall django-wepack-loader
pip install django-wepack-loader==0.7.0

y desaparecerá el error


Artículos relacionados