In questa parte imposteremo un database Postgres per memorizzare i risultati del nostro conteggio delle parole, nonché SQLAlchemy, un Object Relational Mapper e Alambic per gestire le migrazioni del database.
Bonus gratuito: Fai clic qui per accedere a un video tutorial gratuito di Flask + Python che ti mostra come creare l'app Web Flask, passo dopo passo.
Aggiornamenti:
- 02/09/2020:aggiornato a Python versione 3.8.1 e alle ultime versioni di Psycopg2, Flask-SQLAlchemy e Flask-Migrate. Vedi sotto per i dettagli. Installa e usa in modo esplicito Flask-Script a causa della modifica dell'interfaccia interna di Flask-Migrate.
- 22/03/2016:aggiornato a Python versione 3.5.1 e alle ultime versioni di Psycopg2, Flask-SQLAlchemy e Flask-Migrate. Vedi sotto per i dettagli.
- 22/02/2015:Aggiunto supporto per Python 3.
Ricorda:ecco cosa stiamo costruendo:un'app Flask che calcola le coppie parola-frequenza in base al testo di un determinato URL.
- Parte uno:imposta un ambiente di sviluppo locale e quindi distribuisci sia un ambiente di staging che un ambiente di produzione su Heroku.
- Parte due:configurare un database PostgreSQL insieme a SQLAlchemy e Alembic per gestire le migrazioni. (attuale )
- Parte terza:aggiungi la logica di back-end per raschiare e quindi elaborare il conteggio delle parole da una pagina web utilizzando le librerie Request, BeautifulSoup e Natural Language Toolkit (NLTK).
- Parte quarta:implementare una coda di attività Redis per gestire l'elaborazione del testo.
- Parte cinque:imposta Angular sul front-end per eseguire continuamente il polling del back-end per vedere se l'elaborazione della richiesta è stata completata.
- Parte sei:push al server di staging su Heroku:configurazione di Redis e dettagli su come eseguire due processi (web e worker) su un singolo Dyno.
- Parte 7:aggiorna il front-end per renderlo più intuitivo.
- Parte otto:crea una direttiva angolare personalizzata per visualizzare un grafico di distribuzione della frequenza utilizzando JavaScript e D3.
Ti serve il codice? Prendilo dal repository.
Requisiti di installazione
Strumenti utilizzati in questa parte:
- PostgreSQL (11.6)
- Psycopg2 (2.8.4) - un adattatore Python per Postgres
- Flask-SQLAlchemy (2.4.1) - Estensione Flask che fornisce supporto SQLAlchemy
- Flask-Migrate (2.5.2) - estensione che supporta le migrazioni di database SQLAlchemy tramite Alembic
Per iniziare, installa Postgres sul tuo computer locale, se non lo hai già. Poiché Heroku utilizza Postgres, sarà utile per noi sviluppare localmente sullo stesso database. Se non hai installato Postgres, Postgres.app è un modo semplice per iniziare a funzionare per gli utenti di Mac OS X. Consulta la pagina di download per maggiori informazioni.
Dopo aver installato e avviato Postgres, crea un database chiamato wordcount_dev
da utilizzare come nostro database di sviluppo locale:
$ psql
# create database wordcount_dev;
CREATE DATABASE
# \q
Per utilizzare il nostro database appena creato all'interno dell'app Flask, dobbiamo installare alcune cose:
$ cd flask-by-example
cd
ing nella directory dovrebbe attivare l'ambiente virtuale e impostare le variabili di ambiente che si trovano nel .env
file tramite autoenv, che abbiamo impostato nella 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
Se utilizzi OS X e hai problemi con l'installazione di psycopg2, dai un'occhiata a questo articolo sull'overflow dello stack.
Potrebbe essere necessario installare psycopg2-binary
invece di psycopg2
se l'installazione non riesce.
Aggiorna configurazione
Aggiungi SQLALCHEMY_DATABASE_URI
campo nel Config()
classe nel tuo config.py per impostare la tua app in modo che utilizzi il database appena creato in fase di sviluppo (locale), staging e produzione:
import os
class Config(object):
...
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
Il tuo config.py il file ora dovrebbe assomigliare a questo:
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
Ora, quando la nostra configurazione viene caricata nella nostra app, anche il database appropriato verrà collegato ad essa.
Simile a come abbiamo aggiunto una variabile di ambiente nell'ultimo post, aggiungeremo un DATABASE_URL
variabile. Esegui questo nel terminale:
$ export DATABASE_URL="postgresql:///wordcount_dev"
E poi aggiungi quella riga nel tuo .env file.
Nel tuo app.py importare file SQLAlchemy e connettersi al database:
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()
Modello di dati
Imposta un modello di base aggiungendo un models.py file:
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)
Qui abbiamo creato una tabella per memorizzare i risultati del conteggio delle parole.
Per prima cosa importiamo la connessione al database che abbiamo creato nel nostro app.py file e JSON dai dialetti PostgreSQL di SQLAlchemy. Le colonne JSON sono abbastanza nuove per Postgres e non sono disponibili in tutti i database supportati da SQLAlchemy, quindi è necessario importarle in modo specifico.
Successivamente abbiamo creato un Result()
class e gli ha assegnato un nome di tabella di results
. Quindi impostiamo gli attributi che vogliamo memorizzare per un risultato-
- l'
id
del risultato che abbiamo memorizzato - l'
url
da cui abbiamo contato le parole - un elenco completo di parole che abbiamo contato
- un elenco di parole che abbiamo contato meno le parole chiave (ne parleremo più avanti)
Abbiamo quindi creato un __init__()
metodo che verrà eseguito la prima volta che creiamo un nuovo risultato e, infine, un __repr__()
metodo per rappresentare l'oggetto quando lo interroghiamo.
Migrazione locale
Utilizzeremo Alembic, che fa parte di Flask-Migrate, per gestire le migrazioni di database per aggiornare lo schema di un database.
Nota: Flask-Migrate utilizza il nuovo strumento CLI di Flasks. Tuttavia, questo articolo utilizza l'interfaccia fornita da Flask-Script, utilizzata in precedenza da Flask-Migrate. Per usarlo, devi installarlo tramite:
$ python -m pip install Flask-Script==2.0.6
$ python -m pip freeze > requirements.txt
Crea un nuovo file chiamato 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()
Per poter utilizzare Flask-Migrate abbiamo importato Manager
così come Migrate
e MigrateCommand
al nostro manage.py file. Abbiamo anche importato app
e db
quindi abbiamo accesso ad essi dall'interno dello script.
Per prima cosa, impostiamo la nostra configurazione in modo che il nostro ambiente, basato sulla variabile di ambiente, crei un'istanza di migrazione, con app
e db
come argomenti e imposta un manager
comando per inizializzare un Manager
esempio per la nostra app. Infine, abbiamo aggiunto il db
comando al manager
in modo da poter eseguire le migrazioni dalla riga di comando.
Per eseguire le migrazioni inizializzare Alambic:
$ 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.
Dopo aver eseguito l'inizializzazione del database, vedrai una nuova cartella chiamata "migrazioni" nel progetto. Ciò mantiene la configurazione necessaria affinché Alembic esegua le migrazioni rispetto al progetto. All'interno di "migrazioni" vedrai che ha una cartella chiamata "versioni", che conterrà gli script di migrazione man mano che vengono creati.
Creiamo la nostra prima migrazione eseguendo 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
Ora noterai che nella cartella "versioni" è presente un file di migrazione. Questo file viene generato automaticamente da Alambic in base al modello. Potresti generare (o modificare) questo file tu stesso; tuttavia, nella maggior parte dei casi, il file generato automaticamente funzionerà.
Ora applicheremo gli aggiornamenti al database utilizzando 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
Il database è ora pronto per essere utilizzato nella nostra app:
$ 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)
Migrazione remota
Infine, applichiamo le migrazioni ai database su Heroku. Prima, però, dobbiamo aggiungere i dettagli dei database di staging e produzione a config.py file.
Per verificare se abbiamo un database impostato sul server di staging, esegui:
$ heroku config --app wordcount-stage
=== wordcount-stage Config Vars
APP_SETTINGS: config.StagingConfig
Assicurati di sostituire wordcount-stage
con il nome della tua app di staging.
Dal momento che non vediamo una variabile di ambiente del database, dobbiamo aggiungere l'addon Postgres al server di staging. Per farlo, esegui il seguente 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
è il livello gratuito del componente aggiuntivo Heroku Postgres.
Ora, quando eseguiamo heroku config --app wordcount-stage
di nuovo dovremmo vedere le impostazioni di connessione per il database:
=== 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
Quindi dobbiamo eseguire il commit delle modifiche che hai apportato a git e inviarle al tuo server di staging:
$ git push stage master
Esegui le migrazioni che abbiamo creato per migrare il nostro database di staging utilizzando 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
Nota come abbiamo eseguito solo l'upgrade
, non il init
o migrate
comandi come prima. Abbiamo già il nostro file di migrazione configurato e pronto per l'uso; dobbiamo solo applicarlo al database di Heroku.
Ora facciamo lo stesso per la produzione.
- Configura un database per la tua app di produzione su Heroku, proprio come hai fatto per lo staging:
heroku addons:create heroku-postgresql:hobby-dev --app wordcount-pro
- Invia le modifiche al tuo sito di produzione:
git push pro master
Nota come non devi apportare modifiche al file di configurazione:sta impostando il database in base alDATABASE_URL
appena creato variabile di ambiente. - Applica le migrazioni:
heroku run python manage.py db upgrade --app wordcount-pro
Ora sia i nostri siti di staging che quelli di produzione hanno i loro database configurati, migrati e pronti per l'uso!
Quando si applica una nuova migrazione al database di produzione, potrebbero verificarsi tempi di inattività. Se questo è un problema, puoi configurare la replica del database aggiungendo un database "follower" (comunemente noto come slave). Per ulteriori informazioni, consulta la documentazione ufficiale di Heroku.
Conclusione
Questo è tutto per la parte 2. Se desideri approfondire Flask, dai un'occhiata alla nostra serie di video di accompagnamento:
Bonus gratuito: Fai clic qui per accedere a un video tutorial gratuito di Flask + Python che ti mostra come creare l'app Web Flask, passo dopo passo.
Nella parte 3 creeremo la funzionalità di conteggio delle parole e la invieremo a una coda di attività per gestire l'elaborazione del conteggio delle parole più lunga.
Arrivederci alla prossima. Saluti!
Questo è un pezzo di collaborazione tra Cam Linke, co-fondatore di Startup Edmonton, e la gente di Real Python.