Aplicaciones de una sola página con Vue.js y Flask: API RESTful con Flask

    API RESTful con Flask

    Bienvenido a la cuarta publicación sobre el uso de Vue.js y Flask para el desarrollo web de pila completa. El enfoque de esta publicación será la creación de una API REST de backend utilizando el marco web Flask basado en Python.

    El código de esta publicación está en un repositorio en mi cuenta de GitHub bajo la rama FourthPost.

    Contenido de la serie

    • Seup y familiarización con VueJS
    • Navegando por el enrutador Vue
    • Gestión de estado con Vuex
    • API RESTful con Flask (estás aquí)
    • Integración AJAX con API REST
    • Autenticación JWT
    • Implementación en un servidor privado virtual

    Breve explicación del matraz

    Flask es un micro-framework basado en Python para la creación rápida de prototipos y el desarrollo de aplicaciones web de tamaño pequeño a moderado. Flask ya se ha cubierto en un par de publicaciones anteriores aquí y aquí en Pharos.sh, por lo que no entraré en grandes detalles sobre las partes básicas o comunes de Flask. En su lugar, adoptaré un enfoque más pragmático que se centrará principalmente en la construcción de una API RESTful para alimentar la interfaz con datos, que cubrí en los artículos anteriores a este.

    Andamiaje de los archivos de proyecto de backend

    Empiezo en el directorio / backend creando un entorno virtual Python3 e instalando Flask y algunas otras bibliotecas necesarias.

    $ python -m venv venv
    $ source venv/bin/activate
    (venv) $ pip install Flask Flask-SQLAlchemy Flask-Migrate Flask-Script requests
    

    Una cosa que hace que Flask (y en gran parte todo el ecosistema de Python) sea tan asombroso es la gran cantidad de paquetes bien diseñados disponibles en PyPI. A continuación se muestra una breve explicación de las bibliotecas que instalé y su uso previsto.

    • Matraz: micro marco web
    • Flask-SQLAlchemy: ORM basado en SQLAlchemy con una salsa increíble específica de Flask empaquetada con él
    • Flask-Migrate: biblioteca de migración de bases de datos
    • Flask-Script: paquete extremadamente útil para interactuar con una aplicación Flask desde la línea de comandos
    • solicitudes: un paquete útil para realizar solicitudes de red que usaré para probar la API REST

    En el directorio / backend hago algunos archivos nuevos llamados manage.py y appserver.py. Además, crearé un nuevo directorio dentro de / backend que se convertirá en mi aplicación Flask «surveyyapi». Dentro del directorio surveyyapi hago los archivos __init__.py, models.py, application.py y api.py. Esto da como resultado una estructura de directorio que comienza en / backend así (omitiendo el directorio venv).

    ├── manage.py
    ├── appserver.py
    └── surveyapi
        ├── __init__.py
        ├── api.py
        ├── application.py
        ├── config.py
        └── models.py
    

    A continuación se muestra una breve descripción de para qué se utilizará cada archivo:

    • manage.py: acceso a la instancia de la aplicación Flask para varios comandos Flask-Script
    • appserver.py: script de inicio para ejecutar la aplicación surveyyapi
    • surveyyapi /: la aplicación backend Flask
    • __init__.py: convierte el directorio surveyyapi en un paquete Python válido
    • api.py: para definir puntos finales de ruta de API REST capaces de consumir y producir solicitudes y respuestas JSON
    • application.py: para crear una instancia de la aplicación Flask
    • config.py: contiene opciones de configuración para la aplicación Flask
    • models.py: para definir clases que servirán como objetos de datos para la aplicación de encuestas, como Encuesta, Pregunta y Elección

    Crear una fábrica de aplicaciones

    Comenzaré a codificar la aplicación surveyyapi definiendo algunas configuraciones dentro de config.py así:

    """
    config.py
    - settings for the flask application object
    """
    
    class BaseConfig(object):
        DEBUG = True
        SQLALCHEMY_DATABASE_URI = 'sqlite:///survey.db'
        SQLALCHEMY_TRACK_MODIFICATIONS = False
        # used for encryption and session management
        SECRET_KEY = 'mysecretkey'
    

    Esta clase de configuración define un SQLALCHEMY_DATABASE_URI URI de conexión de la base de datos de la aplicación a una base de datos SQLite de un solo archivo llamada survey.db. También proporciona SECRET_KEY opción de configuración que se utiliza para el cifrado.

    Dentro de application.py, crearé lo que se conoce como una función de fábrica de aplicaciones, que hace exactamente lo que parece, crea una instancia de aplicación Flask. Además de crear una instancia de Flask, también obtiene el BaseConfig objeto y registra el plano de rutas API que haré a continuación.

    """
    application.py
    - creates a Flask app instance and registers the database object
    """
    
    from flask import Flask
    
    def create_app(app_name="SURVEY_API"):
        app = Flask(app_name)
        app.config.from_object('surveyapi.config.BaseConfig')
        from surveyapi.api import api
        app.register_blueprint(api, url_prefix="/api")
        return app
    

    API de planos

    A continuación, me moveré al módulo api.py donde puedo definir un Blueprint objeto llamado api que contiene rutas RESTful. Para mantener las cosas simples, comenzaré simplemente definiendo una función de vista simple llamada say_hello() asociado con el punto final /api/hello/<string:name>/. los <string:name> parte de la URL es una variable de cadena dinámica que se pasa a la función de vista say_hello(name) como un parámetro de función que utilizo en el mensaje de respuesta JSON que se devuelve.

    """
    api.py
    - provides the API endpoints for consuming and producing
      REST requests and responses
    """
    
    from flask import Blueprint, jsonify, request
    
    api = Blueprint('api', __name__)
    
    @api.route('/hello/<string:name>/')
    def say_hello(name):
        response = { 'msg': "Hello {}".format(name) }
        return jsonify(response)
    

    Punto de entrada del servidor de desarrollo y validación de la configuración

    Para probar esto, necesito agregar un par de líneas de código en appserver.py para crear una instancia de la aplicación. Esto me permite iniciar el servidor de desarrollo de Flask llamando al run() método en el app ejemplo.

    """
    appserver.py
    - creates an application instance and runs the dev server
    """
    
    if __name__ == '__main__':
        from surveyapi.application import create_app
        app = create_app()
        app.run()
    

    Para ejecutar el servidor de desarrollo Flask, todo lo que tengo que hacer es iniciar el intérprete de Python y alimentarlo con el script appserver.py como se muestra a continuación.

    (venv) $ python appserver.py
     * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
     * Restarting with stat
     * Debugger is active!
     * Debugger PIN: 676-284-544
    

    Ahora, para probar el nuevo punto final, en una nueva terminal con el entorno virtual activado, encenderé un intérprete de Python y haré una solicitud GET para http://localhost:5000/api/hello/adam/ utilizando el requests paquete.

    (venv) $ python
    >>> import requests
    >>> response = requests.get('http://localhost:5000/api/hello/adam/')
    >>> print(response.json())
    {'msg': 'Hello adam'}
    

    Definición de la capa de datos

    Ahora que he verificado que tengo una aplicación de Flask en funcionamiento, puedo concentrarme en construir la capa de datos con la ayuda del ORM de Flask-SQLAlchemy. La implementación de una capa de datos requerirá escribir algunas clases de datos dentro de models.py como:

    • Encuesta: este es el objeto de nivel superior que contendrá una o más preguntas junto con sus opciones
    • Pregunta: objetos que pertenecen a un objeto de encuesta y contienen opciones
    • Elección: objetos que pertenecen a una pregunta y representan opciones para la pregunta de la encuesta.

    Estas clases de datos plantearán campos que en gran parte imitarán los descritos anteriormente en los artículos sobre la construcción de la aplicación frontend Vue.js, pero estos se asignarán a las tablas de la base de datos donde se conservarán sus datos.

    """
    models.py
    - Data classes for the surveyapi application
    """
    
    from datetime import datetime
    from flask_sqlalchemy import SQLAlchemy
    
    db = SQLAlchemy()
    
    class Survey(db.Model):
        __tablename__ = 'surveys'
    
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.Text)
        created_at = db.Column(db.DateTime, default=datetime.utcnow)
        questions = db.relationship('Question', backref="survey", lazy=False)
    
        def to_dict(self):
            return dict(id=self.id,
                        name=self.name,
                        created_at=self.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                        questions=[question.to_dict() for question in self.questions])
    
    class Question(db.Model):
        __tablename__ = 'questions'
    
        id = db.Column(db.Integer, primary_key=True)
        text = db.Column(db.String(500), nullable=False)
        created_at = db.Column(db.DateTime, default=datetime.utcnow)
        survey_id = db.Column(db.Integer, db.ForeignKey('surveys.id'))
        choices = db.relationship('Choice', backref="question", lazy=False)
    
        def to_dict(self):
            return dict(id=self.id,
                        text=self.text,
                        created_at=self.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                        survey_id=self.survey_id,
                        choices=[choice.to_dict() for choice in self.choices])
    
    class Choice(db.Model):
        __tablename__ = 'choices'
    
        id = db.Column(db.Integer, primary_key=True)
        text = db.Column(db.String(100), nullable=False)
        selected = db.Column(db.Integer, default=0)
        created_at = db.Column(db.DateTime, default=datetime.utcnow)
        question_id = db.Column(db.Integer, db.ForeignKey('questions.id'))
    
        def to_dict(self):
            return dict(id=self.id,
                        text=self.text,
                        created_at=self.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                        question_id=self.question_id)
    
    

    Como se mencionó anteriormente, estoy usando la extensión específica de Flask de SQLAlchemy llamada Matraz-SQLAlchemy para alimentar el ORM para esta aplicación. Me gusta Flask-SQLAlchemy porque tiene una API bastante Pythonic y proporciona valores predeterminados sensibles para definir y trabajar con clases de datos.

    Cada clase hereda de la SQLAlchemy Model clase base que proporciona métodos de utilidad intuitivos y legibles para interactuar con los datos almacenados en la base de datos. Además, cada clase se compone de una serie de campos de clase que se traducen en campos de la tabla de la base de datos según lo especificado por SQLAlchemy Column clase y tipo asociado (es decir, Integer, String, DateTime, Text, …).

    También notará que cada clase tiene un to_dict() método. Este método será útil para serializar los datos de los modelos en JSON al enviarlos por cable al cliente frontend.

    El siguiente paso en la lista es registrar el objeto SQLAlchemy, db, con el objeto de aplicación Flask en application.py.

    """
    application.py
    - creates a Flask app instance and registers the database object
    """
    
    from flask import Flask
    
    def create_app(app_name="SURVEY_API"):
        app = Flask(app_name)
        app.config.from_object('surveyapi.config.BaseConfig')
    
        from surveyapi.api import api
        app.register_blueprint(api, url_prefix="/api")
    
        from surveyapi.models import db
        db.init_app(app)
    
        return app
    
    

    Lo último que me gustaría hacer es reunir a los Frasco-Script y Frasco-Migrar paquetes de extensión dentro del módulo manage.py para permitir migraciones. Este práctico módulo, manage.py, reunirá las clases de datos que acabo de definir y las vinculará al contexto de la aplicación junto con la maquinaria Flask-Migrate y Flask-Script.

    """
    manage.py
    - provides a command line utility for interacting with the
      application to perform interactive debugging and setup
    """
    
    from flask_script import Manager
    from flask_migrate import Migrate, MigrateCommand
    
    from surveyapi.application import create_app
    from surveyapi.models import db, Survey, Question, Choice
    
    app = create_app()
    
    migrate = Migrate(app, db)
    manager = Manager(app)
    
    # provide a migration utility command
    manager.add_command('db', MigrateCommand)
    
    # enable python shell with application context
    @manager.shell
    def shell_ctx():
        return dict(app=app,
                    db=db,
                    Survey=Survey,
                    Question=Question,
                    Choice=Choice)
    
    if __name__ == '__main__':
        manager.run()
    

    Estoy logrando dos cosas en este fragmento de código anterior. Primero, estoy creando una instancia del objeto de aplicación Flask para que proporcione contexto al Migrate(app, db) y Manage(app) instancias. Luego estoy agregando un comando al manager objeto que me permite crear y ejecutar migraciones desde la línea de comando así:

    (venv) $ python manage.py db init
    
    • Inicialice el directorio de migraciones junto a la aplicación y el archivo de base de datos de survey.db
    (venv) $ python manage.py db migrate
    
    • Cree un archivo de migración inicial para traducir las clases en models.py a SQL que generará las tablas correspondientes
    (venv) $ python manage.py db upgrade
    
    • Ejecute la migración para actualizar la base de datos con las tablas descritas en el paso anterior

    Lo último que estoy haciendo en el módulo manage.py es crear otro comando personalizado utilizando @manager.shell para decorar un shell_ctx() función que devuelve una palabra clave de asignación de dict a la app y db objetos junto con el Survey, Question y, Choice clases de datos.

    Ahora aprovecharé la utilidad de este comando de utilidad de shell para demostrar cómo trabajar con el ORM Flask-SQLAlchemy dentro del intérprete de Python que produce.

    (venv) $ python manage.py shell
    (venv) Adams-MacBook-Pro:backend adammcquistan$ python manage.py shell
    >>> survey = Survey(name="Dogs")
    >>> question = Question(text="What is your favorite dog?")
    >>> question.choices = [Choice(text="Beagle"), Choice(text="Rottweiler"), Choice(text="Labrador")]
    >>> question2 = Question(text="What is your second favorite dog?")
    >>> question2.choices = [Choice(text="Beagle"), Choice(text="Rottweiler"), Choice(text="Labrador")]
    >>> survey.questions = [question, question2]
    >>> db.session.add(survey)
    >>> db.session.commit()
    >>> surveys = Survey.query.all()
    >>> for s in surveys:
    ...     print('Survey(id={}, name={})'.format(s.id, s.name))
    ...     for q in s.questions:
    ...             print('  Question(id={}, text={})'.format(q.id, q.text))
    ...             for c in q.choices:
    ...                     print('    Choice(id={}, text={})'.format(c.id, c.text))
    ...
    Survey(id=1, name=Dogs)
      Question(id=1, text=What is your favorite dog?)
        Choice(id=1, text=Beagle)
        Choice(id=3, text=Labrador)
        Choice(id=2, text=Rottweiler)
      Question(id=2, text=What is your second favorite dog?)
        Choice(id=4, text=Beagle)
        Choice(id=6, text=Labrador)
        Choice(id=5, text=Rottweiler)
    

    Eso es bastante hábil, ¿verdad?

    No me refiero solo a la sintaxis elegante y legible del ORM, sino a la capacidad increíblemente poderosa de activar un intérprete de Python que contiene el contexto de la aplicación para hacer pequeños experimentos rápidos con los modelos de su aplicación. No puedo decirle cuánto aumento de productividad me ha proporcionado esto al crear aplicaciones de backend, y le sugiero seriamente que lo utilice al hacer lo mismo.

    Completando la API RESTful

    Ahora que la capa de acceso a datos está construida, puedo enfocar mi atención en completar la implementación necesaria para la API RESTful. Esto manejará el consumo y la devolución de los recursos de la aplicación, como los datos de la encuesta, la pregunta y la elección. Los casos de uso requeridos de la API RESTful incluyen lo siguiente:

    • Obtenga todas las encuestas junto con sus preguntas y opciones
    • Obtenga una sola encuesta junto con sus preguntas y opciones
    • Cree una nueva encuesta junto con sus preguntas y opciones específicas
    • Actualizar las opciones de respuesta de una encuesta una vez que se ha realizado una encuesta

    Para empezar, seguiré adelante e importaré todas las clases de datos junto con SQLAlchemy db instancia para que tenga acceso a ellos. En la parte superior de api.py agrego las siguientes importaciones:

    """
    api.py
    - provides the API endpoints for consuming and producing
      REST requests and responses
    """
    
    from flask import Blueprint, jsonify, request
    from .models import db, Survey, Question, Choice
    

    En cuanto a los puntos finales de recursos reales, comenzaré codificando la capacidad de obtener todos los recursos de la encuesta. Dentro de api.py necesito reemplazar el /hello/<string:name>/ punto final con la ruta /surveys/ punto final y surveys() función de visualización.

    @api.route('/surveys/')
    def surveys():
        surveys = Survey.query.all()
        return jsonify({ 'surveys': [s.to_dict() for s in surveys] })
    

    Si el servidor de desarrollo aún se está ejecutando, una vez que guarde los archivos del proyecto, el servidor debería recargarse automáticamente y actualizar todos los cambios. Si no, entonces corriendo (venv) $ python appserver.py iniciará el servidor. Ahora en otro terminal con el entorno virtual activado puedo usar el requests paquete para probar este nuevo punto final. Sin embargo, me gustaría compartir un consejo profesional sobre cómo mostrar respuestas JSON de una manera más legible mediante el uso de otro paquete de Python impresionante llamado pprint.

    (venv) $ pip install pprint
    (venv) $ python
    >>> import pprint, requests
    >>> pp == pprint.PrettyPrinter()
    >>> resp = requests.get('http://localhost:5000/api/surveys/')
    >>> pp.pprint(resp.json())
    {'surveys': [{
         'created_at': '2018-03-06 03:52:44',
         'id': 1,
         'name': 'Dogs',
         'questions': [{
              'choices': [{
                   'created_at': '2018-03-06 03:52:44',
                   'id': 1,
                   'question_id': 1,
                   'text': 'Beagle'
                  },{
                   'created_at': '2018-03-06 03:52:44',
                   'id': 3,
                   'question_id': 1,
                   'text': 'Labrador'
                  },{
                   'created_at': '2018-03-06 03:52:44',
                   'id': 2,
                   'question_id': 1,
                   'text': 'Rottweiler'}],
                'created_at': '2018-03-06 03:52:44',
                'id': 1,
                'survey_id': 1,
                'text': 'What is your favorite dog?'
             },{
              'choices': [{
                  'created_at': '2018-03-06 03:52:44',
                  'id': 4,
                  'question_id': 2,
                  'text': 'Beagle'
                 },{
                  'created_at': '2018-03-06 03:52:44',
                  'id': 6,
                  'question_id': 2,
                  'text': 'Labrador'
                 },{
                  'created_at': '2018-03-06 03:52:44',
                  'id': 5,
                  'question_id': 2,
                  'text': 'Rottweiler'}],
              'created_at': '2018-03-06 03:52:44',
              'id': 2,
              'survey_id': 1,
              'text': 'What is your second favorite dog?'}]}
        ]}
    

    A continuación, implementaré la funcionalidad para obtener una sola encuesta por su id con el extremo de la URL /surveys/id/ y ver la función survey(id). Inmediatamente después de la surveys() Función de vista API coloco el siguiente código:

    @api.route('/surveys/<int:id>/')
    def survey(id):
        survey = Survey.query.get(id)
        return jsonify({ 'survey': survey.to_dict() })
    

    Nuevamente, guardaré los archivos y probaré el nuevo punto final de la API para asegurarme de que brinde una respuesta válida.

    >>> resp = requests.get('http://localhost:5000/api/surveys/1/')
    >>> pp.pprint(resp.json())
    {'survey': {
         'created_at': '2018-03-06 03:52:44',
         'id': 1,
         'name': 'Dogs',
         'questions': [{
              'choices': [{
                   'created_at': '2018-03-06 03:52:44',
                   'id': 1,
                   'question_id': 1,
                   'text': 'Beagle'
                  },{
                   'created_at': '2018-03-06 03:52:44',
                   'id': 3,
                   'question_id': 1,
                   'text': 'Labrador'
                  },{
                   'created_at': '2018-03-06 03:52:44',
                   'id': 2,
                   'question_id': 1,
                   'text': 'Rottweiler'}],
                'created_at': '2018-03-06 03:52:44',
                'id': 1,
                'survey_id': 1,
                'text': 'What is your favorite dog?'
             },{
              'choices': [{
                  'created_at': '2018-03-06 03:52:44',
                  'id': 4,
                  'question_id': 2,
                  'text': 'Beagle'
                 },{
                  'created_at': '2018-03-06 03:52:44',
                  'id': 6,
                  'question_id': 2,
                  'text': 'Labrador'
                 },{
                  'created_at': '2018-03-06 03:52:44',
                  'id': 5,
                  'question_id': 2,
                  'text': 'Rottweiler'}],
              'created_at': '2018-03-06 03:52:44',
              'id': 2,
              'survey_id': 1,
              'text': 'What is your second favorite dog?'}]}
        }
    

    Hasta ahora, solo he utilizado el método de ruta HTTP GET predeterminado adecuado para obtener datos de las API RESTful. Sin embargo, para las dos últimas funciones, necesitaré utilizar los métodos HTTP POST y PUT para los puntos finales /api/surveys/ y /api/surveys/id/, respectivamente. Usaré el método HTTP POST para crear nuevas encuestas y el método HTTP PUT para actualizar una encuesta existente con un nuevo conjunto de opciones de respuesta seleccionadas.

    Para el /api/surveys/ ruta Necesitaré agregar un parámetro de método a la declaración de ruta para especificar que acepta los métodos GET y POST, methods=('GET','POST'). Además modificaré el cuerpo del surveys() Ver función para diferenciar el tipo de método y agregar la capacidad de guardar una nueva encuesta en la base de datos.

    @api.route('/surveys/', methods=('GET', 'POST'))
    def fetch_surveys():
        if request.method == 'GET':
            surveys = Survey.query.all()
            return jsonify({ 'surveys': [s.to_dict() for s in surveys] })
        elif request.method == 'POST':
            data = request.get_json()
            survey = Survey(name=data['name'])
            questions = []
            for q in data['questions']:
                question = Question(text=q['text'])
                question.choices = [Choice(text=c['text'])
                                    for c in q['choices']]
                questions.append(question)
            survey.questions = questions
            db.session.add(survey)
            db.session.commit()
            return jsonify(survey.to_dict()), 201
    

    Nuevamente, querré guardar el proyecto y probarlo para asegurarme de tener un recurso de ahorro de encuestas completamente funcional.

    >>> import json
    >>> survey = {
    ...   'name': 'Cars',
    ...   'questions': [{
    ...     'text': 'What is your favorite car?',
    ...     'choices': [
    ...       { 'text': 'Corvette' },
    ...       { 'text': 'Mustang' },
    ...       { 'text': 'Camaro' }]
    ...   }, {
    ...     'text': 'What is your second favorite car?',
    ...     'choices': [
    ...       { 'text': 'Corvette' },
    ...       { 'text': 'Mustang' },
    ...       { 'text': 'Camaro' }]
    ...   }]
    ... }
    >>> headers = {'Content-type': 'application/json'}
    >>> resp = requests.post('http://localhost:5000/api/surveys/', headers=headers, data=json.dumps(survey))
    >>> resp.status_code
    201
    

    La última pieza a implementar es la capacidad de actualizar una encuesta existente con nuevas selecciones de respuesta a la encuesta. Nuevamente, tendré que agregar los métodos de GET y PUT al /api/surveys/id/ definición de ruta, methods=('GET', 'PUT'). Entonces actualizo el survey(id) Ver función para actualizar las opciones de preguntas de la encuesta asociada especificadas como seleccionadas en el cuerpo JSON de la solicitud PUT.

    @api.route('/surveys/<int:id>/', methods=('GET', 'PUT'))
    def survey(id):
        if request.method == 'GET':
            survey = Survey.query.get(id)
            return jsonify({ 'survey': survey.to_dict() })
        elif request.method == 'PUT':
            data = request.get_json()
            for q in data['questions']:
                choice = Choice.query.get(q['choice'])
                choice.selected = choice.selected + 1
            db.session.commit()
            survey = Survey.query.get(data['id'])
            return jsonify(survey.to_dict()), 201
    

    Por último, necesito guardar todos mis archivos y hacer una prueba final como esta:

    >>> survey_choices = {
    ...   'id': 1,
    ...   'name': 'Dogs',
    ...   'questions': [
    ...     { 'id': 1, 'choice': 1 },
    ...     { 'id': 2, 'choice': 5 }]
    ... }
    >>> headers = {'Content-type': 'application/json'}
    >>> resp = requests.put('http://localhost:5000/api/surveys/1/', data=json.dumps(survey_choices), headers=headers)
    >>> resp.status_code()
    201
    

    Conclusión

    En este artículo he cubierto cómo implementar una API RESTful simple, bastante básica, usando Flask de acuerdo con la siguiente tabla:

    Funcionalidad del método de ruta

    / api / survey /OBTENERRecuperar todas las encuestas
    / api / survey /ENVIARCrea una nueva encuesta
    / api / survey / id /OBTENERRecuperar una encuesta por id
    / api / survey / id /PONERActualizar las selecciones de opciones de una encuesta

    En el próximo artículo, demostraré cómo integrar la aplicación frontend Vue.js para que pueda consumir y enviar actualizaciones de datos al backend de Flask.

    Como siempre, gracias por leer y no dude en comentar o criticar a continuación.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *