sql >> Database teknologi >  >> RDS >> Database

Flask efter eksempel – Opsætning af Postgres, SQLAlchemy og Alembic

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.

  1. Del 1:Opsæt et lokalt udviklingsmiljø og implementer derefter både et iscenesættelses- og et produktionsmiljø på Heroku.
  2. Del to:Opsæt en PostgreSQL-database sammen med SQLAlchemy og Alembic til at håndtere migreringer. (aktuel )
  3. 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.
  4. Fjerde del:Implementer en Redis-opgavekø til at håndtere tekstbehandlingen.
  5. Femte del:Indstil Angular på front-end for løbende at polle back-end for at se, om anmodningen er færdigbehandlet.
  6. 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.
  7. Syvende del:Opdater front-end for at gøre den mere brugervenlig.
  8. 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 gemte
  • url 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.

  1. Opret en database til din produktionsapp på Heroku, ligesom du gjorde til iscenesættelse:heroku addons:create heroku-postgresql:hobby-dev --app wordcount-pro
  2. 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 nyoprettede DATABASE_URL miljøvariabel.
  3. 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.



  1. Lav kumulativ sum i view oracle

  2. Hvordan viser jeg en MySQL-fejl i PHP for en lang forespørgsel, der afhænger af brugerens input?

  3. Hvordan medtager man nul/0 resultater i COUNT aggregat?

  4. SQL COUNT() for begyndere