W tej części skonfigurujemy bazę danych Postgres do przechowywania wyników naszej liczby słów, a także SQLAlchemy, Object Relational Mapper i Alembic do obsługi migracji baz danych.
Bezpłatny bonus: Kliknij tutaj, aby uzyskać dostęp do bezpłatnego samouczka wideo Flask + Python, który pokazuje, jak krok po kroku zbudować aplikację internetową Flask.
Aktualizacje:
- 02.09.2020:Aktualizacja do wersji Pythona 3.8.1 oraz najnowszych wersji Psycopg2, Flask-SQLAlchemy i Flask-Migrate. Zobacz poniżej szczegóły. Jawnie zainstaluj i używaj Flask-Script ze względu na zmianę wewnętrznego interfejsu Flask-Migrate.
- 22.03.2016:Aktualizacja do wersji Pythona 3.5.1 oraz najnowszych wersji Psycopg2, Flask-SQLAlchemy i Flask-Migrate. Zobacz poniżej szczegóły.
- 22.02.2015:Dodano obsługę Pythona 3.
Pamiętaj:oto, co tworzymy – aplikacja Flask, która oblicza pary słowo-częstotliwość na podstawie tekstu z danego adresu URL.
- Część pierwsza:skonfiguruj lokalne środowisko programistyczne, a następnie wdroż zarówno środowisko pomostowe, jak i produkcyjne w Heroku.
- Część druga:skonfiguruj bazę danych PostgreSQL wraz z SQLAlchemy i Alembic do obsługi migracji. (aktualny )
- Część trzecia:dodaj logikę zaplecza, aby zeskrobać, a następnie przetworzyć liczbę słów ze strony internetowej za pomocą bibliotek żądań, BeautifulSoup i Natural Language Toolkit (NLTK).
- Część czwarta:Zaimplementuj kolejkę zadań Redis do obsługi przetwarzania tekstu.
- Część piąta:skonfiguruj Angular na froncie, aby stale odpytywać zaplecze, aby sprawdzić, czy żądanie zostało przetworzone.
- Część szósta:Prześlij na serwer pomostowy w Heroku – konfiguracja Redis i szczegółowe omówienie sposobu uruchamiania dwóch procesów (sieciowego i roboczego) na jednym Dyno.
- Część siódma:zaktualizuj interfejs, aby był bardziej przyjazny dla użytkownika.
- Część ósma:Utwórz niestandardową dyrektywę kątową, aby wyświetlić wykres rozkładu częstotliwości za pomocą JavaScript i D3.
Potrzebujesz kodu? Pobierz go z repozytorium.
Wymagania dotyczące instalacji
Narzędzia użyte w tej części:
- PostgreSQL (11.6)
- Psycopg2 (2.8.4) — adapter Pythona dla Postgresa
- Flask-SQLAlchemy (2.4.1) - rozszerzenie Flask, które zapewnia obsługę SQLAlchemy
- Flask-Migrate (2.5.2) - rozszerzenie obsługujące migracje baz danych SQLAlchemy przez Alembic
Aby rozpocząć, zainstaluj Postgres na swoim lokalnym komputerze, jeśli jeszcze go nie masz. Ponieważ Heroku korzysta z Postgresa, dobrze będzie, jeśli będziemy rozwijać się lokalnie na tej samej bazie danych. Jeśli nie masz zainstalowanego Postgresa, Postgres.app to łatwy sposób na uruchomienie i uruchomienie dla użytkowników Mac OS X. Więcej informacji znajdziesz na stronie pobierania.
Po zainstalowaniu i uruchomieniu Postgresa utwórz bazę danych o nazwie wordcount_dev
do wykorzystania jako nasza lokalna baza danych deweloperskich:
$ psql
# create database wordcount_dev;
CREATE DATABASE
# \q
Aby korzystać z naszej nowo utworzonej bazy danych w aplikacji Flask, musimy zainstalować kilka rzeczy:
$ cd flask-by-example
cd
Wejście do katalogu powinno aktywować środowisko wirtualne i ustawić zmienne środowiskowe znajdujące się w .env
plik przez autoenv, który ustawiliśmy w części 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
Jeśli korzystasz z OS X i masz problemy z instalacją psycopg2, zapoznaj się z tym artykułem Stack Overflow.
Może być konieczne zainstalowanie psycopg2-binary
zamiast psycopg2
jeśli instalacja się nie powiedzie.
Aktualizuj konfigurację
Dodaj SQLALCHEMY_DATABASE_URI
pole do Config()
klasę w pliku config.py plik, aby ustawić aplikację tak, aby korzystała z nowo utworzonej bazy danych w fazie rozwoju (lokalnie), pomostowej i produkcyjnej:
import os
class Config(object):
...
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
Twój config.py plik powinien teraz wyglądać tak:
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
Teraz, gdy nasza konfiguracja zostanie załadowana do naszej aplikacji, odpowiednia baza danych zostanie również z nim połączona.
Podobnie jak dodaliśmy zmienną środowiskową w ostatnim poście, dodamy DATABASE_URL
zmienny. Uruchom to w terminalu:
$ export DATABASE_URL="postgresql:///wordcount_dev"
A następnie dodaj tę linię do pliku .env plik.
W Twoim app.py plik zaimportuj SQLAlchemy i połącz się z bazą danych:
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()
Model danych
Skonfiguruj podstawowy model, dodając models.py plik:
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)
Tutaj stworzyliśmy tabelę do przechowywania wyników liczenia słów.
Najpierw importujemy połączenie z bazą danych, które utworzyliśmy w naszym app.py oraz JSON z dialektów PostgreSQL firmy SQLAlchemy. Kolumny JSON są dość nowe w Postgresie i nie są dostępne w każdej bazie danych obsługiwanej przez SQLAlchemy, więc musimy je specjalnie zaimportować.
Następnie utworzyliśmy Result()
klasy i przypisał jej nazwę tabeli results
. Następnie ustawiamy atrybuty, które chcemy przechowywać dla wyniku-
id
wyników, które zapisaliśmyurl
że policzyliśmy słowa z- pełna lista słów, które policzyliśmy
- lista słów, które policzyliśmy, minus słowa stop (więcej o tym później)
Następnie utworzyliśmy __init__()
metoda, która zostanie uruchomiona za pierwszym razem, gdy utworzymy nowy wynik, a na koniec __repr__()
metoda do reprezentowania obiektu, gdy o niego pytamy.
Migracja lokalna
Zamierzamy użyć Alembic, który jest częścią Flask-Migrate, do zarządzania migracjami baz danych w celu aktualizacji schematu bazy danych.
Uwaga: Flask-Migrate korzysta z nowego narzędzia CLI Flasks. Jednak ten artykuł korzysta z interfejsu udostępnionego przez Flask-Script, który był wcześniej używany przez Flask-Migrate. Aby z niego skorzystać, musisz go zainstalować za pomocą:
$ python -m pip install Flask-Script==2.0.6
$ python -m pip freeze > requirements.txt
Utwórz nowy plik o nazwie 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()
Aby korzystać z Flask-Migrate, zaimportowaliśmy Manager
jak również Migrate
i MigrateCommand
do naszego manage.py plik. Zaimportowaliśmy również app
i db
więc mamy do nich dostęp z poziomu skryptu.
Najpierw ustawiamy naszą konfigurację tak, aby nasze środowisko — na podstawie zmiennej środowiskowej — tworzyło migrowaną instancję z app
i db
jako argumenty i skonfiguruj manager
polecenie do zainicjowania manager
przykład dla naszej aplikacji. Na koniec dodaliśmy db
polecenie do manager
abyśmy mogli uruchomić migracje z wiersza poleceń.
Aby uruchomić migracje, zainicjuj 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.
Po uruchomieniu inicjalizacji bazy danych zobaczysz w projekcie nowy folder o nazwie „migracje”. To zawiera konfigurację niezbędną dla Alembic do uruchamiania migracji w projekcie. Wewnątrz „migrations” zobaczysz, że ma folder o nazwie „versions”, który będzie zawierał tworzone skrypty migracji.
Stwórzmy naszą pierwszą migrację, uruchamiając migrate
polecenie.
$ 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
Teraz zauważysz, że w folderze „wersje” znajduje się plik migracji. Ten plik jest automatycznie generowany przez Alembic na podstawie modelu. Możesz samodzielnie wygenerować (lub edytować) ten plik; jednak w większości przypadków wystarczy automatycznie wygenerowany plik.
Teraz zastosujemy aktualizacje do bazy danych za pomocą db upgrade
polecenie:
$ 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
Baza danych jest teraz gotowa do użycia w naszej aplikacji:
$ 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)
Zdalna migracja
Na koniec zastosujmy migracje do baz danych na Heroku. Najpierw jednak musimy dodać szczegóły baz danych pomostowych i produkcyjnych do config.py plik.
Aby sprawdzić, czy mamy skonfigurowaną bazę danych na serwerze pomostowym, uruchom:
$ heroku config --app wordcount-stage
=== wordcount-stage Config Vars
APP_SETTINGS: config.StagingConfig
Pamiętaj, aby zastąpić wordcount-stage
z nazwą Twojej aplikacji testowej.
Ponieważ nie widzimy zmiennej środowiskowej bazy danych, musimy dodać dodatek Postgres do serwera pomostowego. Aby to zrobić, uruchom następujące polecenie:
$ 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
to darmowa warstwa dodatku Heroku Postgres.
Teraz, gdy uruchamiamy heroku config --app wordcount-stage
ponownie powinniśmy zobaczyć ustawienia połączenia z bazą danych:
=== 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
Następnie musimy zatwierdzić zmiany, które wprowadziłeś w git i przesłać na serwer pomostowy:
$ git push stage master
Uruchom migracje, które utworzyliśmy w celu migracji naszej tymczasowej bazy danych za pomocą heroku run
polecenie:
$ 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
Zwróć uwagę, że uruchomiliśmy tylko upgrade
, a nie init
lub migrate
polecenia jak wcześniej. Mamy już skonfigurowany i gotowy do pracy plik migracji; wystarczy zastosować go do bazy danych Heroku.
Zróbmy teraz to samo dla produkcji.
- Skonfiguruj bazę danych dla aplikacji produkcyjnej w Heroku, tak jak w przypadku testowania:
heroku addons:create heroku-postgresql:hobby-dev --app wordcount-pro
- Prześlij swoje zmiany do witryny produkcyjnej:
git push pro master
Zwróć uwagę, że nie musisz wprowadzać żadnych zmian w pliku konfiguracyjnym - ustawia on bazę danych na podstawie nowo utworzonegoDATABASE_URL
zmienna środowiskowa. - Zastosuj migracje:
heroku run python manage.py db upgrade --app wordcount-pro
Teraz zarówno nasze witryny testowe, jak i produkcyjne mają skonfigurowane bazy danych i są migrowane – i gotowe do pracy!
Po zastosowaniu nowej migracji do produkcyjnej bazy danych może wystąpić przestój. Jeśli jest to problem, możesz skonfigurować replikację bazy danych, dodając bazę danych „follower” (powszechnie znaną jako podrzędna). Więcej informacji na ten temat znajdziesz w oficjalnej dokumentacji Heroku.
Wniosek
To tyle w części 2. Jeśli chcesz zagłębić się w Flask, obejrzyj naszą towarzyszącą serię filmów:
Bezpłatny bonus: Kliknij tutaj, aby uzyskać dostęp do bezpłatnego samouczka wideo Flask + Python, który pokazuje, jak krok po kroku zbudować aplikację internetową Flask.
W części 3 zamierzamy zbudować funkcję liczenia słów i wysłać ją do kolejki zadań, aby poradzić sobie z dłużej działającym przetwarzaniem liczenia słów.
Do zobaczenia następnym razem. Pozdrawiam!
To dzieło powstałe dzięki współpracy Cam Linke, współzałożycielki Startup Edmonton, i ludzi z Real Pythona.