I den här delen kommer vi att skapa en Postgres-databas för att lagra resultaten av våra ordräkningar samt SQLAlchemy, en Object Relational Mapper och Alembic för att hantera databasmigreringar.
Gratis bonus: Klicka här för att få tillgång till en gratis självstudievideo för Flask + Python som visar hur du bygger en webbapp för Flask, steg för steg.
Uppdateringar:
- 02/09/2020:Uppgraderad till Python version 3.8.1 samt de senaste versionerna av Psycopg2, Flask-SQLAlchemy och Flask-Migrate. Se nedan för detaljer. Installera och använd Flask-Script explicit på grund av förändring av det interna gränssnittet för Flask-Migrate.
- 03/22/2016:Uppgraderad till Python version 3.5.1 samt de senaste versionerna av Psycopg2, Flask-SQLAlchemy och Flask-Migrate. Se nedan för mer information.
- 02/22/2015:Lade till stöd för Python 3.
Kom ihåg:Det här är vad vi bygger - En Flask-app som beräknar ord-frekvenspar baserat på texten från en given webbadress.
- Del ett:Skapa en lokal utvecklingsmiljö och distribuera sedan både en iscensättning och en produktionsmiljö på Heroku.
- Del två:Konfigurera en PostgreSQL-databas tillsammans med SQLAlchemy och Alembic för att hantera migrering. (aktuell )
- Del tre:Lägg till back-end-logiken för att skrapa och bearbeta sedan ordantalet från en webbsida med hjälp av biblioteken för begäran, BeautifulSoup och Natural Language Toolkit (NLTK).
- Del fyra:Implementera en Redis-uppgiftskö för att hantera textbehandlingen.
- Del fem:Ställ in Angular på front-end för att kontinuerligt polla back-end för att se om begäran är klar.
- Del sex:Push till staging-servern på Heroku - ställ in Redis och beskriver hur man kör två processer (webb och worker) på en enda Dyno.
- Del sju:Uppdatera gränssnittet för att göra det mer användarvänligt.
- Del åtta:Skapa ett anpassat vinkeldirektiv för att visa ett frekvensfördelningsdiagram med JavaScript och D3.
Behöver du koden? Ta tag i det från arkivet.
Installationskrav
Verktyg som används i denna del:
- PostgreSQL (11.6)
- Psycopg2 (2.8.4) - en Python-adapter för Postgres
- Flask-SQLAlchemy (2.4.1) - Flask-tillägg som ger stöd för SQLAlchemy
- Flask-Migrate (2.5.2) – tillägg som stöder SQLAlchemy-databasmigrering via Alembic
För att komma igång, installera Postgres på din lokala dator, om du inte redan har det. Eftersom Heroku använder Postgres kommer det att vara bra för oss att utveckla lokalt på samma databas. Om du inte har Postgres installerat är Postgres.app ett enkelt sätt att komma igång för Mac OS X-användare. Se nedladdningssidan för mer information.
När du har Postgres installerat och kört, skapa en databas som heter wordcount_dev
att använda som vår lokala utvecklingsdatabas:
$ psql
# create database wordcount_dev;
CREATE DATABASE
# \q
För att kunna använda vår nyskapade databas i Flask-appen måste vi installera några saker:
$ cd flask-by-example
cd
ing i katalogen bör aktivera den virtuella miljön och ställa in miljövariablerna som finns i.env
fil via autoenv, som vi satte upp 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
Om du använder OS X och har problem med att installera psycopg2, kolla in den här Stack Overflow-artikeln.
Du kan behöva installera
psycopg2-binary
istället förpsycopg2
om din installation misslyckas.
Uppdatera konfiguration
Lägg till SQLALCHEMY_DATABASE_URI
till Config()
klass i din config.py fil för att ställa in din app att använda den nyskapade databasen i utveckling (lokal), iscensättning och produktion:
import os
class Config(object):
...
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
Din config.py filen ska nu se ut så här:
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
Nu när vår konfiguration har laddats in i vår app kommer lämplig databas också att kopplas till den.
I likhet med hur vi la till en miljövariabel i förra inlägget, kommer vi att lägga till en DATABASE_URL
variabel. Kör detta i terminalen:
$ export DATABASE_URL="postgresql:///wordcount_dev"
Och lägg sedan till den raden i din .env fil.
I din app.py filimportera SQLAlchemy och anslut till 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()
Datamodell
Ställ in en grundläggande modell genom att lägga till 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)
Här skapade vi en tabell för att lagra resultaten av ordräkningarna.
Vi importerar först databasanslutningen som vi skapade i vår app.py fil samt JSON från SQLAlchemys PostgreSQL-dialekter. JSON-kolumner är ganska nya för Postgres och är inte tillgängliga i alla databaser som stöds av SQLAlchemy så vi måste importera den specifikt.
Därefter skapade vi en Result()
klass och tilldelade den ett tabellnamn med results
. Vi ställer sedan in attributen som vi vill lagra för ett resultat-
id
av resultatet vi lagradeurl
som vi räknade orden från- en fullständig lista över ord som vi räknade
- en lista med ord som vi räknade minus stoppord (mer om detta senare)
Vi skapade sedan en __init__()
metod som körs första gången vi skapar ett nytt resultat och slutligen en __repr__()
metod för att representera objektet när vi frågar efter det.
Lokal migrering
Vi kommer att använda Alembic, som är en del av Flask-Migrate, för att hantera databasmigreringar för att uppdatera en databas schema.
Obs! Flask-Migrate använder sig av Flasks nya CLI-verktyg. Den här artikeln använder dock gränssnittet från Flask-Script, som tidigare användes av Flask-Migrate. För att använda det måste du installera det via:
$ python -m pip install Flask-Script==2.0.6 $ python -m pip freeze > requirements.txt
Skapa en ny fil som heter 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()
För att använda Flask-Migrate importerade vi Manager
samt Migrate
och MigrateCommand
till vår manage.py fil. Vi importerade också app
och db
så vi har tillgång till dem från skriptet.
Först ställer vi in vår konfiguration för att få vår miljö - baserat på miljövariabeln - att skapa en migreringsinstans, med app
och db
som argument, och ställ in en manager
kommando för att initiera en Manager
exempel för vår app. Slutligen lade vi till db
kommandot till manager
så att vi kan köra migreringarna från kommandoraden.
Initiera Alembic:
för att köra migreringarna$ 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 databasinitieringen kommer du att se en ny mapp som heter "migrationer" i projektet. Detta innehåller den installation som krävs för att Alembic ska kunna köra migreringar mot projektet. Inuti "migrering" kommer du att se att den har en mapp som heter "versioner", som kommer att innehålla migreringsskripten när de skapas.
Låt oss skapa vår första migrering genom att köra 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 kommer du att märka att det finns en migreringsfil i mappen "versioner". Den här filen är automatiskt genererad av Alembic baserat på modellen. Du kan skapa (eller redigera) den här filen själv; Men i de flesta fall fungerar den automatiskt genererade filen.
Nu kommer vi att tillämpa uppgraderingarna på databasen med 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 är nu klar för oss att använda i vår 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)
Fjärmigrering
Slutligen, låt oss tillämpa migreringarna på databaserna på Heroku. Först måste vi dock lägga till detaljerna om iscensättnings- och produktionsdatabaserna till config.py fil.
För att kontrollera om vi har en databas inställd på staging-servern, kör:
$ heroku config --app wordcount-stage
=== wordcount-stage Config Vars
APP_SETTINGS: config.StagingConfig
Se till att ersätta
wordcount-stage
med namnet på din iscensättningsapp.
Eftersom vi inte ser en databasmiljövariabel måste vi lägga till Postgres-tillägget till staging-servern. För att göra det, kör följande 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
är den kostnadsfria nivån för Heroku Postgres-tillägget.
Nu när vi kör heroku config --app wordcount-stage
igen bör vi se anslutningsinställningarna för 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
Därefter måste vi överföra ändringarna som du har gjort i git och push till din staging-server:
$ git push stage master
Kör migreringarna som vi skapade för att migrera vår mellanlagringsdatabas genom att använda 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
Lägg märke till hur vi bara körde
upgrade
, inteinit
ellermigrate
kommandon som tidigare. Vi har redan vår migreringsfil konfigurerad och redo att gå; vi behöver bara applicera det mot Heroku-databasen.
Låt oss nu göra samma sak för produktionen.
- Sätt upp en databas för din produktionsapp på Heroku, precis som du gjorde för iscensättning:
heroku addons:create heroku-postgresql:hobby-dev --app wordcount-pro
- Överför dina ändringar till din produktionsplats:
git push pro master
Lägg märke till hur du inte behöver göra några ändringar i konfigurationsfilen - det är att ställa in databasen baserat på den nyskapadeDATABASE_URL
miljövariabel. - Använd migreringarna:
heroku run python manage.py db upgrade --app wordcount-pro
Nu har både våra iscensättnings- och produktionsplatser sina databaser konfigurerade och migrerade - och redo att börja!
När du tillämpar en ny migrering till produktionsdatabasen kan det bli stilleståndstid. Om detta är ett problem kan du ställa in databasreplikering genom att lägga till en "följare" (allmänt känd som en slav) databas. För mer om detta, kolla in den officiella Heroku-dokumentationen.
Slutsats
Det var allt för del 2. Om du vill gräva djupare i Flask, kolla in vår medföljande videoserie:
Gratis bonus: Klicka här för att få tillgång till en gratis självstudiekurs för Flask + Python som visar hur du bygger en webbapp för Flask, steg för steg.
I del 3 kommer vi att bygga ordräkningsfunktionen och skicka den till en uppgiftskö för att hantera den längre pågående ordräkningsbehandlingen.
Vi ses nästa gång. Skål!
Detta är ett samarbete mellan Cam Linke, medgrundare av Startup Edmonton, och folket på Real Python.