Cómo integrar Django y React.js, Parte 1
Cuando buscaba la forma de usar ReactJS junto con Django me enfrenté al problema que no sabía como manejar los tokens del lado del cliente, dónde guardarlos, como recuperarlos, como usarlo usarlos, es decir nada.
Así que se me ocurrió usar las sesiones de Django y usar ReactJS dentro de Django. Sin la posibilidad de usar las variables de Django encontré una técnica que me ha servido.
La configuración en Django
Puede ser tu proyecto normal, uno nuevo por ejemplo, con los módulos que acostumbras usar, pero además, con este llamado django-webpack-loader
que nos permite usar Webpack de forma transparente en Django.
Requiere de una configuración adeicional en Django, pero es muy simple de verdad.
WEBPACK_LOADER = {
'DEFAULT': {
'CACHE': DEBUG,
'BUNDLE_DIR_NAME': 'bundles/',
'STATS_FILE': APPS_DIR.child('static', 'webpack-stats.json')
}
}
Solo indicamos si usamos caché, el directorio del bundle, que es el archivo que crea WebPack y por último dónde encontramos el archivo webpack-stats.json
que le dice a Django dónde está React.
La configuración en React
Yo hice una configuración personalizada de WebPack, pero igual la que se crea con create-react-app
puede servir, solo sigue las instrucciones de esa aplicación.
Necesitamos un módulo de node llamado webpack-bundle-tracker
que se encarga de crear el archivo webpack-stats.json
que usamos en Django para ubicar a React. Instálalo como dependencia de desarrollo.
Ahora debemos configurar WebPack, para esto necesitamos indicar, para mayor facilidad, la salida del bundle. Así que debemos agregar en webpack.config.js
lo siguiente:
output: {
path: path.resolve(__dirname, '../cerebro/apps/static/js'),
filename: "[name]-[hash].js",
publicPath: 'http://localhost:8080/static/bundles/'
},
Y en la parte plugins
indicamos que cargue el BundleTracker
:
plugins: [
new BundleTracker({filename: '../cerebro/apps/static/webpack-stats.json'}),
]
Consulta la documentación de WebPack si tienes alguna duda sobre como configurarlo.
La vista
Primero necesitamos una vista que carga una plantilla. Es lo único que hacer, cargar la portada de la aplicación.
Así que en mi aplicación tengo un archivo views.py
que tiene esta vista:
import json
from django.views import View
class Index(View):
template_name = 'index.html'
def get(self, request, *args, **kwargs):
data = {
'perimisos': json.dumps(
sorted(list(request.user.get_all_permissions()))
)
}
return render(request, self.template_name, data)
En el método GET
de esta vista agrega una variable data
que contiene de forma serializada los permisos que tiene el usuario, igual podríamos agregar el usuario o cualquier otro dato, pero debe estar en formato JSON.
La ruta
Esta vista se vuelve la portada en los patrones de búsqueda definidos en urls.py
:
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path, re_path
from core.views import Index
urlpatterns = [
path('ingresar/', auth_views.LoginView.as_view(), name='login'),
path('salir/', auth_views.LogoutView, name='logout'),
path('admin/', admin.site.urls),
path('', Index.as_view(), name="index"),
re_path(r'^(?:.*)/?$', Index.as_view())
]
Como pueden observar, con este método puedo definir rutas que quiero que atienda django, como ingresar
o admin
, pero también defino que la portada y todo lo demás sea atendido por Index
. Esto es importante, la ruta catch all sirve para pasar todas las rutas que no atienda Django a React. Por eso va al final, primero vemos si la atiende Django y si no, se la pasamos a React.
La plantilla
La plantilla incluye la carga de las etiquetas especiales que webpack_loader
pone a nuestra disposición.
Usamos la carga de las hojas de estilo que genera WebPack en el área del encabezado y la carga del JavaScript al final del archivo.
{% load static %}
{% 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">
<title>Cuadro de Mando</title>
{% render_bundle 'app' 'css' %}
<script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"></script>
</head>
<body>
<div id="app"></div>
<script>
window.django = {
csrf: "{{ csrf_token }}",
urls: {
login: "{% url 'login' %}",
logout: "{% url "logout" %}" },
user: {
authenticated: {% autoescape off %}("True" === "{{ user.is_authenticated }}"){% endautoescape %},
username: "{{ request.user.username }}",
full_name: "{{ request.user.get_full_name }}",
last_login: "{{ request.user.last_login }}",
permissions: {% autoescape off %}JSON.parse('{{ permissions }}'){% endautoescape %}
}
};
</script>
{% render_bundle 'app' 'js' %}
</body>
</html>
Lo interesante es que usamos en este archivo las variables de Django que usando JavaScript pasamos al objeto window
del navegador y de esta forma están disponibles como variables globales en React.
Aquí pueden agregar las variables que necesiten, siempre y cuando el resultado final sea un objeto JSON válido. En este ejemplo, le pasados la variable csrf
, hacemos un objeto con algunas URLs y creamos el objeto user
que le dice a React si en esta sesión estamos acreditados o no.
Conclusión
Ahora solo tienes que iniciar tu proyecto de Django y tu proyecto de React y ambos estarán conectados.
El código fuente generado por Django se ve así:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Cuadro de Mando</title>
<link type="text/css" href="http://localhost:8080/static/bundles/css/cmi_styles.css" rel="stylesheet" />
<script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"></script>
</head>
<body>
<div id="app"></div>
<script>
window.django = {
csrf: "VWuQ4KaN9ocP5N4abDWV8ZF7FpGbEXZCReKOzHCUEahQNHBR20jiECrZ1BwnwoiH",
urls: {
login: "/ingresar/",
logout: "/salir/",
staticRoot: "/assets/index.html",
},
user: {
authenticated: ("True" === "False"),
username: "",
full_name: "",
last_login: "",
permissions: JSON.parse('[]')
}
};
</script>
<script type="text/javascript" src="http://localhost:8080/static/bundles/app-04f6b683ac4f718959ff.js" ></script>
</body>
</html>
Y ahí aparece la llamada a React desde Django.