Integrar Django y Vue.js
Tengo 2 años desarrollando en Django, en este tiempo he cambiado de frameworks y librerías de JavaScript buscando optimizar la aplicación al máximo. Vue es el framework que mejor se ha acoplado a mis necesidades de desarrollo y con el que me he sentido más cómodo en el proceso.
El método que usaba en el proceso era implementando las herramientas mediante Bower como package manager e importándolas desde su locación.
django_project
|_ django_project
|_ __init__.py
|_ settings.py
|_ urls.py
|_ wsgi.py
|_ staticfiles
|_ css
|_ js
|_ lib
|_ scss
|_ templates
|_ base.html
|_ index.html
|.bowerrc
|_ bower.json
|_ manage.py
|_ requirements.txt
Problema
Para aplicaciones pequeñas y con la configuración necesaria de seguridad es una buena técnica, pero cuándo se requiere crear una aplicación más compleja, con muchos componentes reactivos y la necesidad de requests constantes, la eficiencia de la aplicación no basta, pues Vue no se ha compilado.
Solución
Antes de continuar hay que tener en claro que ya deberás tener conocimientos de Vue CLI y Django.
Existen Diferentes soluciones para poder trabajar con Vue dentro de Django (incluyendo la anterior). Pero la más eficiente es servir los bundle files de Vue desde los staticfiles del entorno de Django y usando el template compilado.
Entorno
El proceso no varia mucho entre los sistemas operativos ya que al final es uso de Python y npm (también puede ser yarn). Por la versión de Python que yo utilizo, se usará Django 1.11. Esto no afectará el proceso en Python 3.xx.xx con Django 2.
Ubuntu 8.14.16 LTS
python = 2.7.15
npm = 6.4.1
node = 8.10.0
vue = 2.9.6
Hay que iniciar el entorno virtual de toda la vida.
$ cd ~/Documents/Tutorials/
$ cd ENV/ && virtualenv DV && cd ..
$ source ENV/DV/bin/activate
E iniciar un proyecto simple de Django.
(DV)$ pip install django
(DV)$ django-admin.py startproject DjangoVue && cd DjangoVue
(DV)$ pip freeze > requirements.txt
(DV)$ ./manage.py migrate
(DV)$ ./manage.py runserver
Resultado
DjangoVue
|_ DjangoVue
|_ __init__.py
|_ settings.py
|_ urls.py
|_ wsgi.py
|_ db.sqlie3
|_ manage.py
|_ requirements.txt
Hay que iniciar una nueva aplicación de Vue con Webpack. Con la configuración requerida
(DV)$ vue init webpack frontend? Project name frontend
? Project description A Vue.js project
? Author Mauro Nava Luevanos <mauronavaluevanos63@gmail.com>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm
Resultado
DjangoVue
|_ DjangoVue
|_ frontend
|_ build
|_ config
|_ node_modules
|_ src
|_ static
|_ .babelrc
|_ .editorconfig
|_ .eslintignore
|_ .eslintrc.js
|_ .gitignore
|_ .postcssrc.js
|_ index.html
|_ package.json
|_ README.md
|_ db.sqlie3
|_ manage.py
|_ requirements.txt
No es necesario hacer configuraciones del entorno en Webpack a menos que se requiera, pues ya esta configurado por default tanto para desarrollo como producción.
(DV)$ cd frontend/
(DV)$ npm install
(DV)$ npm start
Magia
No es una gran idea correr en el servidor de desarrollo de Vue dentro de Django a la vez, ya que es un embrollo e inestable, además de que sería el equivalente a su uso con Bower.
Es necesario crear los bundles con Webpack, lo cuál ya viene implementado con un script por default. Solo hay que crear los bundle en la carpeta fronted.
(DV)$ npm run build
Este script compila los archivos a JS puro en la carpeta dist.
DjangoVue
|_ DjangoVue
|_ frontend
|_ build
|_ dist
|_ static
|_ index.html
|_ config
|_ node_modules
|_ src
|_ static
|_ .babelrc
|_ .editorconfig
|_ .eslintignore
|_ .eslintrc.js
|_ .gitignore
|_ .postcssrc.js
|_ index.html
|_ package.json
|_ README.md
|_ db.sqlie3
|_ manage.py
|_ requirements.txt
Esta carpeta es la que serviremos en los staticfiles y templates de Django.
Templates
Un error sería creer que se renderizará el index.html que esta directamente en la carpeta de frontend. El index.html que utilizaremos será el que ha sido compilado con los bundles.
En la línea 57 de settings.py (ejemplo de arriba) es dónde colocaremos el directorio correcto.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'frontend/dist') # The correct index.html is located here
],
'APP_DIRS': True,
Para poder verificar que está funcionando correctamente hay que crear una vista simple y agregar la url correspondiente. En mi caso inicialicé una nueva app (CMS) y la registré en las apps instaladas de Django.
DjangoVue
|_ CMS
|_ __init__.py
|_ admin.py
|_ apps.py
|_ models.py
|_ tests.py
|_ urls.py
|_ views.py
|_ DjangoVue
|_ frontend
|_ db.sqlie3
|_ manage.py
|_ requirements.txt
CMS/views.py
CMS/urls.py
DjangoVue/urls.py
Resultado
Como se puede ver, se está renderizando el template correctamente, pero los js y css no se encuentran ya que falta regitrar el directorio de los staticfiles en el entorno de Django.
Staticfiles
Ya casí esta listo, solo falta servir los archivos estáticos para visualizar el contenido de Vue. Hay que registrar las variables de los archivos estáticos dentro de DjangoVue/settings.py.
STATIC_ROOT = os.path.join(BASE_DIR, 'static')STATIC_URL = '/static/'STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'frontend/dist/static/')
# Puedes agregar mas carpetas si es necesario
]
Solo falta agregar las url de los archivos en DjangoVue/urls.py para que pueda detectar correctamente los archivos.
Resultado
Ya está listo todo. Pudes checar el repositorio aquí.
Extra
Si necesitas agregar variables, urls, staticfiles u otra cosa necesaria del entorno de Django en el template, será en el index.html que esta alojado directamente en la carpeta de frontend.
DjangoVue
|_ DjangoVue
|_ frontend
|_ build
|_ dist
|_ config
|_ node_modules
|_ src
|_ static
|_ .babelrc
|_ .editorconfig
|_ .eslintignore
|_ .eslintrc.js
|_ .gitignore
|_ .postcssrc.js
|_ index.html <--- Este template
|_ package.json
|_ README.md
|_ db.sqlie3
|_ manage.py
|_ requirements.txt
Para poder consumir la información de Django es recomendable el desarrollo de la API con Graphene ó Django Rest Framework y el consumo con Axios, pero esto es otra historia.