arrow_back Volver
Inicio keyboard_arrow_right Artículos keyboard_arrow_right Artículo

Integrar Vue.Js con Flask

Eduardo Ismael Garcia

Full Stack Developer at Código Facilito.

av_timer 6 Min. de lectura

remove_red_eye 21929 visitas

calendar_today 09 Enero 2019

En este ocasión aprenderemos a integrar Vue.Js, uno de los frameworks frontend más populares en la actualidad, junto con el micro framewok Flask, de tal forma que podamos desarrollar aplicaciones web robustas en muy poco tiempo y con muy pocas líneas de código 😎.

Bien, no perdamos más tiempo y comencemos.

Instalación

Para este post será necesario contar con Flask y Vue instalado en tu equipo de computo. Si tú aun no cuentas con estas tecnologías, no te preocupes 😃.

Para generar nuestra aplicación Vue nos apoyaremos de vue-cli. La instalación la lograremos al ejecutar la siguiente sentencia.

$ npm install -g vue-cli

Para poder consumir servicios web utilizando JS haremos uso de la librería axios. Su instalación es muy sencilla.

npm install --save axios

La instalación de Flask lo haremos a través de pip.

$ pip install flask

Yo de forma personal te recomiendo que hagas la instalación de flask, y de cualquier otra librería de Python, mediante un entorno virtual, de esta forma podemos definir de forma concretas las dependencias entre un proyecto y otro, evitando conflictos de versiones entre librerías.

Crear un entorno virtual mediante virtual_env.

virtualenv -p python3 nombre_entorno_virtual

Activar entorno virtual

source nombre_entorno_virtual/bin/active

Desactivar entorno virtual

deactivate

Aplicación Frontend

Bien, una vez tengamos instalado todo lo necesario el siguiente paso será crear una nueva carpeta, carpeta en donde alojaremos nuestro servidor Flask y nuestro proyecto en Vue. En mi caso la carpeta tendrá por nombre flask_tutorial, tú puedes colocar el nombre que tú desees.

Dentro de la carpeta creamos nuestro projecto Vue.

$ mkdir flask_tutorial
$ cd flask_tutorial
$ vue init webpack fronted

Mi configuración para el proyecto queda de la siguiente manera.

? Project name fronted
? Project description A Vue.js project
? Author Eduardo Ismael García Pérez <[email protected]>
? 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

Probemos que el servidor funciones correctamente.

cd frontend
npm install

npm run dev

Una vez el servidor se encuentre a la escucha, debemos ir a nuestro navegador e ingresar a localhost:8080. Si todo ha salido bien obtendremos la siguiente salida.

El siguiente paso será crear una nueva vista, de tal forma que posteriormente nuestro servidor Flask pueda servirlas.

Dentro de nuestra carpeta components (frontend/src) creamos un nuevo archivo con extensión .vue, en mi caso el archivo tendrá por nombre Home.vue

<template>
  <div>
    <p>Hola Mundo desde Vue!</p>
  </div>
</template>

Habilitamos una nueva url para desplegar dicho componenten, para ello modificamos nuestro archivo index.js (frontend/src/router). El archivo pudiese quedar de la siguiente manera.

import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/components/Main'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'main',
      component: Main
    },
  ],
  mode: 'history'
})

En este caso importo el nuevo componente y le asignó la url principal ('/'), también eliminó todo lo relacionado al componente default 😛, finalmente asignó 'history' al atributo mode.

Si ingresamos a localhost:8080 obtendremos nuestro Hola Mundo desde Vue!, junto con el icono de Vue, algo que en lo personal no me agrada 😰.

Para quitar dicho icono nos situamos en el archivo App.vue (frontend/src) y eliminamos la etiqueta tag.

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

Cool, ahora agreguemos un poco de interacción a nuestro componente, haremos que este consuma un servicio Web, servicio que por supuesto lo proveerá nuestro servidor Flask.

El componenten quedaría de la siguiente manera (Home.vue).

<template>
  <div>
    <p>{{ mensaje }}</p>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  name: 'Main',
  data () {
    return {
      mensaje: 'Sin mensaje!'
    }
  },
  methods: {
    getMensaje () {
      const path = 'http://localhost:5000/api/v1.0/mensaje'
      axios.get(path).then((respuesta) => {
        this.mensaje = respuesta.data
      })
      .catch((error) => {
        console.log(error)
      })
    }
  },
  created () {
    this.getMensaje()
  }
}
</script>

Nos apoyamos de la librería axios para poder consumir un servicio web. Una vez obtengamos respuesta por parte del servidor actualizamos nuestro atributo mensaje, atributo que se encuentra bind en nuestro template. Todos esto lo logramos gracias a las promesas, si no te encuentras familiarizado con este termino, no te preocupes, te recomiendo tomes este taller, el cual sin duda te será de mucha ayuda.

Si actualizamos nuestra página web obtendremos como resultado Sin mensaje!, valor por default de nuestra variable mensaje, esto se debe a que no existe (por ahora) servidor a la escucha en el puerto 5000 que pueda responder a nuestra petición.

Servidor Flask

Nuestro servidor será muy sencillo. Dentro de la carpeta flask_tutorial, fuera de la carpeta frontend, creamos un nuevo archivo con extensión .py, en mi caso el archivo tendrá por nombre server.py.

El archivo quedaría de la siguiente manera.

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api/v1.0/mensaje')
def create_task():
    return jsonify('Hola mundo desde Flask')

if __name__ == '__main__':
    app.run(debug=True)

Si ejecutamos e ingresamos a localhost:5000/api/v1.0/mensaje a través de nuestro navegador obtendremos el siguiente resultado.

Sin embargo 😰, si ingresamos a http://localhost:8080/ seguiremos obteniendo el mensaje Sin mensaje! :P. Si inspeccionamos la consola nos toparemos con pequeño error:

'http://localhost:5000/api/v1.0/mensaje' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Este error se debe a que nuestro servidor Flask no puede responder peticiones de servidores externos, en este caso Vue se encuentra en a la escucha en el puerto 8080 y Flask en el 5000.

Para solucionar este problema instalaremos flask-cors.

pip install -U flask-cors

En el archivo server.py quedaría de la siguiente manera.

from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})

De esta forma nuestro servidor podrá responder cualquier petición por parte de Vue a la API. Si actualizamos localhost:8080 obtendremos la siguiente salida 😎.

Fusion

Hasta este punto tenemos dos servidores que trabajan en conjunto, y esta bien, sin embargo, lo ideal sería unicamente contar con uno y que este sea el encargado de proporcionar el API y servir las diferentes vistas, es por ello que en este último apartado haremos que sea nuestro servidor Flask el encargado proporcionar el HTML para el pintado.

Lo primero que haremos será compilar nuestro proyecto Vue, para ello ejecutamos la siguiente sentencia (Dentro de la carpeta frontend).

npm run build

Al nosotros compilar el proyecto obtendremos como resultado una nueva carpeta llamada dist, es en esta carpeta donde se encuentran todo nuestro frontend (HTML, CSS, y JS).

Ahora nos situamos en nuestro archivo server.py, ya que haremos un par de modificaciones.

from flask import render_template

... Código anterior

app = Flask(__name__,
            static_folder = "./fronted/dist/static",
            template_folder = "./fronted/dist")

... Código anterior

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def dender_vue(path):
    return render_template("index.html")

... Código posterior

Primero modificamos los parámetros static_folder y template_folder, indicando que los archivos los encontraremos en una nueva ruta, dentro de la carpeta frontend/dist. Posteriormente indicamos que cualquier petición sobre '/' (raíz) será manejada por el archivo index.html. Con esta función podemos tener la n cantidad de urls definidas en Vue y Flask será capaz de servirlas sin ningún problema.

Issues

Aun que esto es funcional (de forma local), al momento de realizar el deploy a un servidor será necesario realizar un par de modificaciones al proyecto.

Cuando nos encontremos en producción no será necesario hacer uso de la clase CORS.

Una muy buena idea es agregar a nuestro archivo .gitgnore la carpeta dist, de tal forma que sea el mismo proceso de deploy quien construya los archivos. Si así lo deseamos podemos generar dicha carpeta fuera de frontend, basta con modificar el archivo index.js.

index: path.resolve(__dirname, '../../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../../dist'),

En este caso la carpeta dist se genera al mismo nivel que el archivo server.py.

Aumenta tu productividad con GitHub Copilot

  • done Curso GRATUITO
  • done Regístrate antes del 31 de Marzo
  • done Obtén una ventaja competitiva
  • done Aprovecha la IA a tu favor
Más información