En esta parte, vamos a configurar una base de datos de Postgres para almacenar los resultados de nuestros conteos de palabras, así como SQLAlchemy, un mapeador relacional de objetos y Alambic para manejar las migraciones de la base de datos.
Bonificación gratuita: Haga clic aquí para obtener acceso a un videotutorial gratuito de Flask + Python que le muestra cómo crear la aplicación web de Flask, paso a paso.
Actualizaciones:
- 09/02/2020:actualizado a la versión 3.8.1 de Python, así como a las últimas versiones de Psycopg2, Flask-SQLAlchemy y Flask-Migrate. Consulte a continuación para obtener más información. Instale y use explícitamente Flask-Script debido al cambio de la interfaz interna de Flask-Migrate.
- 22/03/2016:actualizado a la versión 3.5.1 de Python, así como a las últimas versiones de Psycopg2, Flask-SQLAlchemy y Flask-Migrate. Vea a continuación para obtener más detalles.
- 22/02/2015:Se agregó compatibilidad con Python 3.
Recuerda:esto es lo que estamos creando:una aplicación Flask que calcula pares de palabras y frecuencias en función del texto de una URL determinada.
- Primera parte:configure un entorno de desarrollo local y luego implemente un entorno de ensayo y uno de producción en Heroku.
- Segunda parte:configure una base de datos PostgreSQL junto con SQLAlchemy y Alambic para manejar las migraciones. (actual )
- Tercera parte:agregue la lógica de back-end para raspar y luego procesar los recuentos de palabras de una página web utilizando las bibliotecas de solicitudes, BeautifulSoup y Natural Language Toolkit (NLTK).
- Cuarta parte:implementar una cola de tareas de Redis para manejar el procesamiento de texto.
- Quinta parte:Configure Angular en el front-end para sondear continuamente el back-end para ver si la solicitud ha terminado de procesarse.
- Sexta parte:enviar al servidor de prueba en Heroku:configurar Redis y detallar cómo ejecutar dos procesos (web y trabajador) en un solo Dyno.
- Séptima parte:actualice la interfaz para que sea más fácil de usar.
- Octava parte:Cree una directiva angular personalizada para mostrar un gráfico de distribución de frecuencia usando JavaScript y D3.
¿Necesitas el código? Cógelo del repositorio.
Requisitos de instalación
Herramientas utilizadas en esta parte:
- PostgreSQL (11.6)
- Psycopg2 (2.8.4):un adaptador de Python para Postgres
- Flask-SQLAlchemy (2.4.1):extensión de Flask que proporciona compatibilidad con SQLAlchemy
- Flask-Migrate (2.5.2):extensión que admite migraciones de bases de datos SQLAlchemy a través de Alembic
Para comenzar, instale Postgres en su computadora local, si aún no lo tiene. Dado que Heroku usa Postgres, será bueno para nosotros desarrollar localmente en la misma base de datos. Si no tiene Postgres instalado, Postgres.app es una manera fácil de ponerlo en marcha para los usuarios de Mac OS X. Consulte la página de descarga para obtener más información.
Una vez que haya instalado y ejecutado Postgres, cree una base de datos llamada wordcount_dev
para usar como nuestra base de datos de desarrollo local:
$ psql
# create database wordcount_dev;
CREATE DATABASE
# \q
Para utilizar nuestra base de datos recién creada dentro de la aplicación Flask, debemos instalar algunas cosas:
$ cd flask-by-example
cd
Entrar en el directorio debería activar el entorno virtual y establecer las variables de entorno que se encuentran en .env
archivo a través de autoenv, que configuramos en la parte 1.
$ python -m pip install psycopg2==2.8.4 Flask-SQLAlchemy===2.4.1 Flask-Migrate==2.5.2
$ python -m pip freeze > requirements.txt
Si tiene OS X y tiene problemas para instalar psycopg2, consulte este artículo sobre desbordamiento de pila.
Es posible que deba instalar psycopg2-binary
en lugar de psycopg2
si su instalación falla.
Actualizar configuración
Agregue SQLALCHEMY_DATABASE_URI
campo a Config()
clase en su config.py archivo para configurar su aplicación para usar la base de datos recién creada en desarrollo (local), preparación y producción:
import os
class Config(object):
...
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
Su config.py El archivo ahora debería verse así:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
DEBUG = False
TESTING = False
CSRF_ENABLED = True
SECRET_KEY = 'this-really-needs-to-be-changed'
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
class ProductionConfig(Config):
DEBUG = False
class StagingConfig(Config):
DEVELOPMENT = True
DEBUG = True
class DevelopmentConfig(Config):
DEVELOPMENT = True
DEBUG = True
class TestingConfig(Config):
TESTING = True
Ahora, cuando nuestra configuración se cargue en nuestra aplicación, la base de datos adecuada también se conectará a ella.
Similar a cómo agregamos una variable de entorno en la última publicación, vamos a agregar un DATABASE_URL
variable. Ejecute esto en la terminal:
$ export DATABASE_URL="postgresql:///wordcount_dev"
Y luego agregue esa línea en su .env archivo.
En tu app.py importar archivo SQLAlchemy y conectarse a la base de datos:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os
app = Flask(__name__)
app.config.from_object(os.environ['APP_SETTINGS'])
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
from models import Result
@app.route('/')
def hello():
return "Hello World!"
@app.route('/<name>')
def hello_name(name):
return "Hello {}!".format(name)
if __name__ == '__main__':
app.run()
Modelo de datos
Configure un modelo básico agregando un models.py archivo:
from app import db
from sqlalchemy.dialects.postgresql import JSON
class Result(db.Model):
__tablename__ = 'results'
id = db.Column(db.Integer, primary_key=True)
url = db.Column(db.String())
result_all = db.Column(JSON)
result_no_stop_words = db.Column(JSON)
def __init__(self, url, result_all, result_no_stop_words):
self.url = url
self.result_all = result_all
self.result_no_stop_words = result_no_stop_words
def __repr__(self):
return '<id {}>'.format(self.id)
Aquí creamos una tabla para almacenar los resultados de los conteos de palabras.
Primero importamos la conexión de base de datos que creamos en nuestro app.py archivo, así como JSON de los dialectos PostgreSQL de SQLAlchemy. Las columnas JSON son bastante nuevas para Postgres y no están disponibles en todas las bases de datos compatibles con SQLAlchemy, por lo que debemos importarlas específicamente.
Luego creamos un Result()
clase y le asignó un nombre de tabla de results
. Luego establecemos los atributos que queremos almacenar para un resultado-
- el
id
del resultado que almacenamos - la
url
que contamos las palabras de - una lista completa de palabras que contamos
- una lista de palabras que contamos menos palabras vacías (más sobre esto más adelante)
Luego creamos un __init__()
método que se ejecutará la primera vez que creamos un nuevo resultado y, finalmente, un __repr__()
para representar el objeto cuando lo consultamos.
Migración local
Vamos a usar Alembic, que es parte de Flask-Migrate, para administrar las migraciones de bases de datos para actualizar el esquema de una base de datos.
$ python -m pip install Flask-Script==2.0.6
$ python -m pip freeze > requirements.txt
Cree un nuevo archivo llamado manage.py :
import os
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app import app, db
app.config.from_object(os.environ['APP_SETTINGS'])
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':
manager.run()
Para usar Flask-Migrate importamos Manager
así como Migrate
y MigrateCommand
a nuestro manage.py expediente. También importamos app
y db
por lo que tenemos acceso a ellos desde el script.
Primero, configuramos nuestra configuración para que nuestro entorno, basado en la variable de entorno, cree una instancia de migración, con app
y db
como argumentos y configurar un manager
comando para inicializar un Manager
instancia para nuestra aplicación. Finalmente, agregamos el db
comando al manager
para que podamos ejecutar las migraciones desde la línea de comandos.
Para ejecutar las migraciones, inicialice Alembic:
$ python manage.py db init
Creating directory /flask-by-example/migrations ... done
Creating directory /flask-by-example/migrations/versions ... done
Generating /flask-by-example/migrations/alembic.ini ... done
Generating /flask-by-example/migrations/env.py ... done
Generating /flask-by-example/migrations/README ... done
Generating /flask-by-example/migrations/script.py.mako ... done
Please edit configuration/connection/logging settings in
'/flask-by-example/migrations/alembic.ini' before proceeding.
Después de ejecutar la inicialización de la base de datos, verá una nueva carpeta llamada "migraciones" en el proyecto. Esto contiene la configuración necesaria para que Alambic ejecute migraciones contra el proyecto. Dentro de "migraciones" verá que tiene una carpeta llamada "versiones", que contendrá los scripts de migración a medida que se crean.
Vamos a crear nuestra primera migración ejecutando migrate
comando.
$ python manage.py db migrate
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'results'
Generating /flask-by-example/migrations/versions/63dba2060f71_.py
... done
Ahora notará que en su carpeta de "versiones" hay un archivo de migración. Este archivo es generado automáticamente por Alembic basado en el modelo. Puede generar (o editar) este archivo usted mismo; sin embargo, en la mayoría de los casos, el archivo generado automáticamente servirá.
Ahora aplicaremos las actualizaciones a la base de datos usando db upgrade
comando:
$ python manage.py db upgrade
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 63dba2060f71, empty message
La base de datos ya está lista para que la usemos en nuestra aplicación:
$ psql
# \c wordcount_dev
You are now connected to database "wordcount_dev" as user "michaelherman".
# \dt
List of relations
Schema | Name | Type | Owner
--------+-----------------+-------+---------------
public | alembic_version | table | michaelherman
public | results | table | michaelherman
(2 rows)
# \d results
Table "public.results"
Column | Type | Modifiers
----------------------+-------------------+------------------------------------------------------
id | integer | not null default nextval('results_id_seq'::regclass)
url | character varying |
result_all | json |
result_no_stop_words | json |
Indexes:
"results_pkey" PRIMARY KEY, btree (id)
Migración remota
Finalmente, apliquemos las migraciones a las bases de datos en Heroku. Sin embargo, primero debemos agregar los detalles de las bases de datos de preparación y producción a config.py archivo.
Para verificar si tenemos una base de datos configurada en el servidor de ensayo, ejecute:
$ heroku config --app wordcount-stage
=== wordcount-stage Config Vars
APP_SETTINGS: config.StagingConfig
Asegúrate de reemplazar wordcount-stage
con el nombre de su aplicación de prueba.
Como no vemos una variable de entorno de base de datos, debemos agregar el complemento de Postgres al servidor de ensayo. Para hacerlo, ejecute el siguiente comando:
$ heroku addons:create heroku-postgresql:hobby-dev --app wordcount-stage
Creating postgresql-cubic-86416... done, (free)
Adding postgresql-cubic-86416 to wordcount-stage... done
Setting DATABASE_URL and restarting wordcount-stage... done, v8
Database has been created and is available
! This database is empty. If upgrading, you can transfer
! data from another database with pg:copy
Use `heroku addons:docs heroku-postgresql` to view documentation.
hobby-dev
es el nivel gratuito del complemento Heroku Postgres.
Ahora, cuando ejecutamos heroku config --app wordcount-stage
nuevamente deberíamos ver la configuración de conexión para la base de datos:
=== wordcount-stage Config Vars
APP_SETTINGS: config.StagingConfig
DATABASE_URL: postgres://azrqiefezenfrg:Zti5fjSyeyFgoc-U-yXnPrXHQv@ec2-54-225-151-64.compute-1.amazonaws.com:5432/d2kio2ubc804p7
A continuación, debemos confirmar los cambios que ha realizado en git y enviarlos a su servidor de ensayo:
$ git push stage master
Ejecute las migraciones que creamos para migrar nuestra base de datos provisional mediante heroku run
comando:
$ heroku run python manage.py db upgrade --app wordcount-stage
Running python manage.py db upgrade on wordcount-stage... up, run.5677
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 63dba2060f71, empty message
Observe cómo solo ejecutamos la upgrade
, no el init
o migrate
comandos como antes. Ya tenemos nuestro archivo de migración configurado y listo para funcionar; solo necesitamos aplicarlo en la base de datos de Heroku.
Ahora hagamos lo mismo para la producción.
- Configure una base de datos para su aplicación de producción en Heroku, tal como lo hizo para la puesta en escena:
heroku addons:create heroku-postgresql:hobby-dev --app wordcount-pro
- Envíe sus cambios a su sitio de producción:
git push pro master
Observe cómo no tiene que hacer ningún cambio en el archivo de configuración:está configurando la base de datos en función delDATABASE_URL
recién creado variable de entorno. - Aplicar las migraciones:
heroku run python manage.py db upgrade --app wordcount-pro
Ahora, tanto nuestros sitios de preparación como de producción tienen sus bases de datos configuradas y migradas, ¡y listas para funcionar!
Cuando aplica una nueva migración a la base de datos de producción, puede haber tiempo de inactividad. Si esto es un problema, puede configurar la replicación de la base de datos agregando una base de datos "seguidora" (comúnmente conocida como esclava). Para obtener más información al respecto, consulte la documentación oficial de Heroku.
Conclusión
Eso es todo para la parte 2. Si desea profundizar en Flask, consulte la serie de videos que la acompañan:
Bonificación gratuita: Haga clic aquí para obtener acceso a un videotutorial gratuito de Flask + Python que le muestra cómo crear la aplicación web de Flask, paso a paso.
En la Parte 3, crearemos la función de conteo de palabras y la enviaremos a una cola de tareas para lidiar con el procesamiento de conteo de palabras de mayor duración.
Nos vemos la próxima vez. ¡Salud!
Esta es una pieza de colaboración entre Cam Linke, cofundador de Startup Edmonton, y la gente de Real Python.