Django + Vue.Js (PARTE I)

Actualmente he encontrado mi mejor stack de desarrollo en la combinación de Django y Vue.JS. Por un lado, Django es un framework basado en Python que fomenta el desarrollo rápido de un Back-End. Y Vue.JS es un potente framework progresivo y basado en componentes que de algún modo adopta lo mejor de Angular2 y React para desarrollar Front-End de la manera más simple posible.

Django y Vue.JS

La forma más frecuente de utilizar Django con Vue.JS es desarrollando una API Restful en Django, aunque también es posible utilizar GraphQL con Graphene (espero contarlo próximamente).

Para Iniciar un proyecto nuevo de API-Rest en Django es necesario hacer lo siguiente:

# Creamos un entorno virtual para trabajar, basado en la versión 3 de Python
virtualenv -python=`which python3` venv
# activamos el nuevo entorno virtualenv
source venv/bin/activate
# Instalamos el framework DJANGO
source venv/bin/activate
# Creamos un nuevo proyecto con una aplicación
django-admin startproject miproyecto

Esto creará una carpeta miproyecto con un archivo manage.py y una carpeta miproyecto que contiene el fichero settings.py. Dicho fichero contiene todas las configuraciones del nuevo proyecto.

En Django un proyecto si divide en pequeñas aplicaciones, y nosotros vamos a crear una sóla, necesaria para el ejemplo que te voy a mostrar:

python manage.py startapp articulos

Esta última orden creará una aplicación llamada articulos, para la cual creará dentro del proyecto una carpeta llamada así que contendrá los archivos:

  • models.py: Los modelos de la base de datos.
  • admin.py: configuración de la interfaz de administración generada por Django.
  • tests.py: Pruebas unitarias y de integración.
  • views.py: Las vistas.
  • ... y la carpeta migrations.

Cada vez que creamos una nueva aplicación, debemos indicarlo en el fichero settings.py del proyecto:

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

Creación de un modelo sencillo y migración

Vamos a crear el modelo para la aplicación Artículo, de la siguiente forma:

# Create your models here.
class Articulo(models.Model):
    articulo_encabezado = models.CharField(max_length=255)
    articulo_texto = models.TextField()

Esto nos obliga a realizar una migración de los datos:

./manage.py makemigrations
./manage.py migrate

La salida de esta orden, si todo sale bien, será así:

Migrations for ‘articulo’:
 Articulo/migrations/0001_initial.py
 — Create model Articulo

Instalar Django-rest-framework

Llego el momento de orientar nuestra aplicación Django para que funcione como una API-Rest. Para ello necesitamos incluir esta librería:

pip install djangorestframework

Debemos decirle a nuestro proyecto que incluya dicha librería a través de settings.py:

INSTALLED_APPS = [
 django.contrib.admin,
 django.contrib.auth,
 django.contrib.contenttypes,
 django.contrib.sessions,
 django.contrib.messages,
 django.contrib.staticfiles,
 articulo,
 rest_framework
]

Serializer, Viewset and Routers

Dentro de la aplicación Artículo vamos a crear un archivo serializers.py que como su nombre indica contiene serializadores para nuestra api-rest. Los serializadores nos permiten convertir datos complejos como consultas a la base de datos o modelos de Django a tipos de datos nativos de Python que luego se podrán representar fácilmente en formato JSON (que es la base de cualquier API-RESTFUL).

Dicho archivo será así:

from rest_framework import serializers
from .models import Articulo
class ArticuloSerializer(serializers.ModelSerializer):
    class Meta:
        model = Articulo
        fields = '__all__'

También tenemos que crear un fichero viewsets.py. Este fichero, en otros frameworks basados en patrón MVC equivaldría a un controlador:

from rest_framework import viewsets
from .models import Articulo
from .serializers import ArticuloSerializer
class ArticuloViewSet(viewsets.ModelViewSet):
    queryset = Articulo.objects.all()
    serializer_class = ArticuloSerializer

Ahora creamos un fichero routers.py dentro de la carpeta de proyecto, donde también están los ficheros settings.py y urls.py.

from rest_framework import routers
from Articulo.viewsets import ArticuloViewSet
router = routers.DefaultRouter()
router.register(rarticulo, ArticuloViewSet)

Éste último fichero, al que se llama enrutador, nos ayuda a decirle a Django que determine automáticamente cómo se deben asignar las URL de una aplicación a la lógica que se ocupa del manejo de las solicitudes entrantes.

El fichero routes.py:

from django.contrib import admin
from django.urls import path, include
from .routers import router
urlpatterns = [
 path(admin/, admin.site.urls),
 path(api/, include(router.urls))
]

Hemos importado nuestro archivo de enrutador para incluir todas las URL integradas en el archivo de enrutadores. Y también hemos agregado a la url la palabra api sólo para separar las URL de la API que ahora se llamarán desde https://mi-ip/api/articulo. Esto es particularmente útil si por un lado tenemos una API y por otro una aplicación diferente sobre el mismo Backend.

Esto nos proporciona las siguientes funcionalidades / llamadas a nuestra API:

Tienes las siguientes api

  • GET: / api / articulo / -> Esto dará todos los artículos

  • POST: / api / artículo -> Esto ayudará a agregar un nuevo artículo

  • DELETE: / api / articulo / {articulo_id} / -> Esto ayudará a eliminar el artículo

  • GET: / api / articulo / {articulo_id} / -> Esto devolverá un artículo en particular

  • PUT: / api / articulo / {articulo_id} / -> Esto ayudará a actualizar todos los campos de un artículo en particular

  • PATCH: / api / articulo / {articulo_id} / -> Esto ayudará a hacer un parche dentro del artículo

Configurando Vue.Js con DJANGO

Dentro de la carpeta Templates, vamos a crear el siguiente layout:

<!DOCTYPE html>
<html lang=”es”>
 <head>
 <meta charset=”utf-8">
 <title>Vue-js | Django</title>
 <meta name=”viewport” content=”width=device-width, initial-scale=1.0">
 <meta name=”description” content=”Ejemplo de vue js y django>
<meta name=”keywords” content=”vuejs, django, crudapp, restapi>
</head>
<body>
 <script src=”https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>
 <script src=”https://cdn.jsdelivr.net/npm/vue-resource@1.3.5"></script>
</body>
</html>

Ahora vamos a modificar el archivo urls.py añadiendo la siguiente línea para acceder a esta plantilla:


    path('articulo', TemplateView.as_view(template_name='index.html')),

Ahora vamos a ver la instancia Vue.js que vamos a crear:


<script type=text/javascript>
 new Vue({
   el: ‘#starting,
   delimiters: [${,}],
   data: {
   articulos: [],
   loading: false,
   currentArticulo: {},
   message: null,
   newArticulo: { articulo_encabezado’: null, articulo_texto’: null },
 },
 mounted: function() {
},
 methods: {
}
 });
 </script>

Es especialmente importante la línea


delimiters: [${,}],

... ya que le dice a Vue.Js que cambie los delimitadores { { } } por ${ } dentro de las plantillas, ya que son los mismos que utiliza Django para su motor de plantillas y se genera un conflicto.

Ahora vamos a agregar a Vue.Js los siguientes métodos que serán los que realicen llamadas a nuestra API RESTFUL:

  • getArticulos → listado de todos los artículos
  • getArticulo → obtener un artículo en particular
  • addArticulo → agregar un nuevo artículo
  • updateArticulo → actualizar un artículo
  • deleteArticulo → eliminar un artículo

El código para estos métodos es el siguiente:


mounted: function() {
 this.getArticulos();
},
methods: {
 getArticulos: function() {
  this.loading = true;
  this.$http.get(/api/Articulo/)
      .then((response) => {
        this.Articulos = response.data;
        this.loading = false;
      })
      .catch((err) => {
       this.loading = false;
       console.log(err);
      })
 },
 getArticulo: function(id) {
  this.loading = true;
  this.$http.get(`/api/Articulo/${id}/`)
      .then((response) => {
        this.currentArticulo = response.data;
        this.loading = false;
      })
      .catch((err) => {
        this.loading = false;
        console.log(err);
      })
 },
 addArticulo: function() {
  this.loading = true;
  this.$http.post(/api/Articulo/,this.newArticulo)
      .then((response) => {
        this.loading = false;
        this.getArticulos();
      })
      .catch((err) => {
        this.loading = false;
        console.log(err);
      })
 },
 updateArticulo: function() {
  this.loading = true;
  this.$http.put(`/api/Articulo/${this.currentArticulo.Articulo_id}/`,     this.currentArticulo)
      .then((response) => {
        this.loading = false;
        this.currentArticulo = response.data;
        this.getArticulos();
      })
      .catch((err) => {
        this.loading = false;
        console.log(err);
      })
 },
 deleteArticulo: function(id) {
  this.loading = true;
  this.$http.delete(`/api/Articulo/${id}/` )
      .then((response) => {
        this.loading = false;
        this.getArticulos();
      })
      .catch((err) => {
        this.loading = false;
        console.log(err);
      })
 }

Veamos por ejemplo como hacer desde la plantilla de Vue.Js una llamada a getArticulos para obtener la lista de artículos desde nuestra API RESTFUL:. Tan sencillo como usar la etiqueta v-for para recorrer la lista / matriz de artículos.

 <body>
     <div id="starting">
       <div class="container">
         <div class="row">
           <h1>Artículos publicados
           <button class="btn btn-success">Crear Artículo</button>
           </h1>
           <table class="table">
             <thead>
               <tr>
                 <th scope="col">#</th>
                 <th scope="col">Encabezado</th>
                 <th scope="col">Acción</th>
               </tr>
             </thead>
             <tbody>
               <tr v-for="Articulo in Articulos">
                 <th scope="row">${Articulo.Articulo_id}</th>
                 <td>${Articulo.Articulo_encabezado}</td>
                 <td>
                   <button class="btn btn-info" v-on:click="getArticulo(Articulo.Articulo_id)">Editar</button>
                   <button class="btn btn-danger" v-on:click="deleteArticulo(Articulo.Articulo_id)">Eliminar</button>
                 </td>
               </tr>
             </tbody>
           </table>
         </div>
       </div>
       <div class="loading" v-if="loading===true">Loading&#8230;</div>
     </div>

CONTINUARÁ...

Índice

  1. Django + Vue.Js (PARTE I)
  2. Django + Vue.Js (PARTE II): autenticación
  3. Django + Vue.Js (PARTE III): autenticación desde Vue.js