I denne del skal vi opsætte en Postgres-database til at gemme resultaterne af vores ordoptælling samt SQLAlchemy, en Object Relational Mapper og Alembic til at håndtere databasemigreringer.
Gratis bonus: Klik her for at få adgang til en gratis Flask + Python-videovejledning, der viser dig, hvordan du bygger Flask-webapp, trin-for-trin.
Opdateringer:
- 02/09/2020:Opgraderet til Python version 3.8.1 samt de seneste versioner af Psycopg2, Flask-SQLAlchemy og Flask-Migrate. Se nedenfor for detaljer. Installer og brug Flask-Script eksplicit på grund af ændring af Flask-Migrate intern grænseflade.
- 22/03/2016:Opgraderet til Python version 3.5.1 samt de seneste versioner af Psycopg2, Flask-SQLAlchemy og Flask-Migrate. Se nedenfor for detaljer.
- 22/02/2015:Tilføjet Python 3-understøttelse.
Husk:Her er, hvad vi bygger - En Flask-app, der beregner ord-frekvenspar baseret på teksten fra en given URL.
- Del 1:Opsæt et lokalt udviklingsmiljø og implementer derefter både et iscenesættelses- og et produktionsmiljø på Heroku.
- Del to:Opsæt en PostgreSQL-database sammen med SQLAlchemy og Alembic til at håndtere migreringer. (aktuel )
- Del tre:Tilføj back-end-logikken for at skrabe og bearbejd derefter ordantallet fra en webside ved hjælp af anmodninger, BeautifulSoup og Natural Language Toolkit (NLTK) bibliotekerne.
- Fjerde del:Implementer en Redis-opgavekø til at håndtere tekstbehandlingen.
- Femte del:Indstil Angular på front-end for løbende at polle back-end for at se, om anmodningen er færdigbehandlet.
- Del seks:Skub til iscenesættelsesserveren på Heroku - opsætning af Redis og detaljer om, hvordan du kører to processer (web og worker) på en enkelt Dyno.
- Syvende del:Opdater front-end for at gøre den mere brugervenlig.
- Del otte:Opret et tilpasset vinkeldirektiv for at vise et frekvensfordelingsdiagram ved hjælp af JavaScript og D3.
Har du brug for koden? Få fat i det fra reposen.
Installationskrav
Værktøjer brugt i denne del:
- PostgreSQL (11.6)
- Psycopg2 (2.8.4) - en Python-adapter til Postgres
- Flask-SQLAlchemy (2.4.1) - Flask-udvidelse, der giver SQLAlchemy-understøttelse
- Flask-Migrate (2.5.2) - udvidelse, der understøtter SQLAlchemy-databasemigrering via Alembic
For at komme i gang skal du installere Postgres på din lokale computer, hvis du ikke allerede har det. Da Heroku bruger Postgres, vil det være godt for os at udvikle lokalt på den samme database. Hvis du ikke har Postgres installeret, er Postgres.app en nem måde at komme i gang for Mac OS X-brugere. Se downloadsiden for mere information.
Når du har Postgres installeret og kørende, skal du oprette en database kaldet wordcount_dev
at bruge som vores lokale udviklingsdatabase:
$ psql
# create database wordcount_dev;
CREATE DATABASE
# \q
For at bruge vores nyoprettede database i Flask-appen skal vi installere et par ting:
$ cd flask-by-example
cd
ing i mappen bør aktivere det virtuelle miljø og indstille miljøvariablerne, der findes i .env
fil via autoenv, som vi satte op i del 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
Hvis du bruger OS X og har problemer med at installere psycopg2, så tjek denne Stack Overflow-artikel.
Du skal muligvis installere psycopg2-binary
i stedet for psycopg2
hvis din installation mislykkes.
Opdater konfiguration
Tilføj SQLALCHEMY_DATABASE_URI
feltet til Config()
klasse i din config.py fil for at indstille din app til at bruge den nyoprettede database i udvikling (lokal), iscenesættelse og produktion:
import os
class Config(object):
...
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
Din config.py filen skulle nu se sådan ud:
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
Når nu vores konfiguration er indlæst i vores app, vil den relevante database også være forbundet til den.
På samme måde som vi tilføjede en miljøvariabel i det sidste indlæg, vil vi tilføje en DATABASE_URL
variabel. Kør dette i terminalen:
$ export DATABASE_URL="postgresql:///wordcount_dev"
Og tilføj derefter den linje i din .env fil.
I din app.py filimport SQLAlchemy og opret forbindelse til databasen:
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()
Datamodel
Konfigurer en grundlæggende model ved at tilføje en models.py fil:
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)
Her har vi lavet en tabel til at gemme resultaterne af ordtællingen.
Vi importerer først den databaseforbindelse, vi oprettede i vores app.py fil samt JSON fra SQLAlchemys PostgreSQL-dialekter. JSON-kolonner er ret nye for Postgres og er ikke tilgængelige i alle databaser, der understøttes af SQLAlchemy, så vi skal importere det specifikt.
Dernæst oprettede vi en Result()
klasse og tildelt den et tabelnavn med results
. Vi indstiller derefter de attributter, som vi vil gemme for et resultat-
id
af resultatet, vi gemteurl
som vi talte ordene fra- en komplet liste over ord, vi har talt
- en liste over ord, som vi har talt minus stopord (mere om dette senere)
Vi oprettede derefter en __init__()
metode, der kører første gang, vi opretter et nyt resultat og til sidst en __repr__()
metode til at repræsentere objektet, når vi forespørger efter det.
Lokal migration
Vi kommer til at bruge Alembic, som er en del af Flask-Migrate, til at administrere databasemigreringer for at opdatere en databases skema.
Bemærk: Flask-Migrate gør brug af Flasks nye CLI-værktøj. Denne artikel bruger dog grænsefladen leveret af Flask-Script, som tidligere blev brugt af Flask-Migrate. For at bruge det skal du installere det via:
$ python -m pip install Flask-Script==2.0.6
$ python -m pip freeze > requirements.txt
Opret en ny fil kaldet 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()
For at bruge Flask-Migrate importerede vi Manager
samt Migrate
og MigrateCommand
til vores manage.py fil. Vi importerede også app
og db
så vi har adgang til dem inde fra scriptet.
Først satte vi vores konfiguration til at få vores miljø - baseret på miljøvariablen - til at oprette en migreringsinstans med app
og db
som argumenterne, og opsæt en manager
kommando for at initialisere en Manager
eksempel for vores app. Til sidst tilføjede vi db
kommando til manager
så vi kan køre migreringerne fra kommandolinjen.
For at køre migreringerne initialiseres 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.
Når du har kørt databaseinitialiseringen, vil du se en ny mappe kaldet "migreringer" i projektet. Dette indeholder den nødvendige opsætning for at Alembic kan køre migreringer mod projektet. Inde i "migreringer" vil du se, at den har en mappe kaldet "versioner", som vil indeholde migreringsscripts, efterhånden som de oprettes.
Lad os oprette vores første migrering ved at køre migrate
kommando.
$ 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
Nu vil du bemærke, at der i din "versioner"-mappe er en migrationsfil. Denne fil er automatisk genereret af Alembic baseret på modellen. Du kan selv generere (eller redigere) denne fil; i de fleste tilfælde vil den automatisk genererede fil dog klare sig.
Nu vil vi anvende opgraderingerne til databasen ved hjælp af db upgrade
kommando:
$ 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
Databasen er nu klar til brug i vores 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)
Fjernmigrering
Lad os endelig anvende migreringerne til databaserne på Heroku. Først skal vi dog tilføje detaljerne om iscenesættelses- og produktionsdatabaserne til config.py fil.
For at kontrollere, om vi har en database sat op på staging-serveren, køres:
$ heroku config --app wordcount-stage
=== wordcount-stage Config Vars
APP_SETTINGS: config.StagingConfig
Sørg for at erstatte wordcount-stage
med navnet på din iscenesættelsesapp.
Da vi ikke ser en databasemiljøvariabel, skal vi tilføje Postgres-tilføjelsen til iscenesættelsesserveren. For at gøre det skal du køre følgende kommando:
$ 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
er det gratis niveau af Heroku Postgres-tilføjelsen.
Nu når vi kører heroku config --app wordcount-stage
igen skulle vi se forbindelsesindstillingerne for databasen:
=== 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
Dernæst skal vi overføre de ændringer, du har foretaget til git og push til din staging-server:
$ git push stage master
Kør de migreringer, vi oprettede for at migrere vores iscenesættelsesdatabase ved at bruge heroku run
kommando:
$ 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
Bemærk, hvordan vi kun kørte upgrade
, ikke init
eller migrate
kommandoer som før. Vi har allerede vores migrationsfil sat op og klar til at gå; vi skal bare anvende det mod Heroku-databasen.
Lad os nu gøre det samme for produktionen.
- Opret en database til din produktionsapp på Heroku, ligesom du gjorde til iscenesættelse:
heroku addons:create heroku-postgresql:hobby-dev --app wordcount-pro
- Skub dine ændringer til dit produktionssted:
git push pro master
Læg mærke til, hvordan du ikke behøver at foretage ændringer i konfigurationsfilen - det er at indstille databasen baseret på den nyoprettedeDATABASE_URL
miljøvariabel. - Anvend migreringerne:
heroku run python manage.py db upgrade --app wordcount-pro
Nu har både vores iscenesættelses- og produktionssteder deres databaser sat op og er migreret - og klar til at gå i gang!
Når du anvender en ny migrering til produktionsdatabasen, kan der være nedetid. Hvis dette er et problem, kan du konfigurere databasereplikering ved at tilføje en "følger"-database (almindeligvis kendt som en slave). For mere om dette, tjek den officielle Heroku-dokumentation.
Konklusion
Det var det for del 2. Hvis du gerne vil grave dybere ned i Flask, så tjek vores medfølgende videoserie:
Gratis bonus: Klik her for at få adgang til en gratis Flask + Python-videovejledning, der viser dig, hvordan du bygger Flask-webapp, trin-for-trin.
I del 3 skal vi bygge ordoptællingsfunktionaliteten og få den sendt til en opgavekø for at håndtere den længerevarende ordoptællingsbehandling.
Vi ses næste gang. Skål!
Dette er et samarbejde mellem Cam Linke, medstifter af Startup Edmonton, og folkene hos Real Python.