blog.miguelgrinberg.com
ãã²ã«ã»ã°ãªã³ããŒã°
<<<å 次>>>
ãã®èšäºã¯ããã²ã«ã°ãªãŒã³ããŒã°ã®æç§æžã®æ°çã®ç¬¬4éšã®ç¿»èš³ã§ãã 以åã®ç¿»èš³ã¯ããã®é¢é£æ§ãé·ã倱ããŸããã
ããã¯ãFlask Mega-Tutorialã·ãªãŒãºã®ç¬¬4çã§ãããŒã¿ããŒã¹ã®æäœæ¹æ³ã説æããŸãã
åèãŸã§ã«ã以äžã¯ãã®ã·ãªãŒãºã®èšäºã®ãªã¹ãã§ãã
泚1ïŒãã®ã³ãŒã¹ã®å€ãããŒãžã§ã³ããæ¢ãã®å Žåã¯ããã¡ããã芧ãã ãã ã
泚2ïŒçªç¶ããã®ããã°ã®ç§ã®ïŒãã²ã«ïŒã®ä»äºãæ¯æããŠå£°ãäžãããå ŽåããŸãã¯åã«èšäºã1é±éåŸ
ã€å¿èããªãå Žåãç§ïŒãã²ã«ã°ãªãŒã³ããŒã°ïŒã¯ãã®ã¬ã€ãã®å®å
šçã«ããã±ãŒãžåãããé»åæžç±ãŸãã¯ãããªãæäŸããŸãã 詳现ã«ã€ããŠã¯ã learn.miguelgrinberg.comãã芧ãã ãã ã
ãã®ç« ã®ãããã¯ã¯éåžžã«éèŠã§ãã ã»ãšãã©ã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãå¹ççã«ååŸã§ããæ°žç¶ããŒã¿ãç¶æããå¿
èŠããããããããŸãã«ããŒã¿ããŒã¹ã®äœæç®çã§ãã
ãã®ç« ã®GitHubãªã³ã¯ïŒ Browse ã Zip ã Diff ã
Flaskã®ããŒã¿ããŒã¹
æ¢ã«èããããšããããšæãã®ã§ãFlaskã¯ããŒã¿ããŒã¹ããã€ãã£ãã«ãµããŒãããŠããŸããã ããã¯ãFlaskãæå³çã«èªçµŠèªè¶³çã§ãªãå€ãã®åéã®1ã€ã§ããããã¯ã1ã€ã«é©å¿ããã®ã§ã¯ãªããã¢ããªã±ãŒã·ã§ã³ã«æé©ãªããŒã¿ããŒã¹ãèªç±ã«éžæã§ããããçŽ æŽãããããšã§ãã
Pythonã«ã¯å€æ°ã®ããŒã¿ããŒã¹ãããããã®å€ãã¯Flaskã¢ããªã±ãŒã·ã§ã³ãšçµ±åãããŠããŸãã ããŒã¿ããŒã¹ã¯ããªã¬ãŒã·ã§ãã«ã¢ãã«ã«å¯Ÿå¿ããã°ã«ãŒããšããã§ãªãã°ã«ãŒãã®2ã€ã®å€§ããªã°ã«ãŒãã«åããããšãã§ããŸãã åŸè
ã®ã°ã«ãŒãã¯ãã°ãã°NoSQLãšåŒã°ããäžè¬çãªSQLãªã¬ãŒã·ã§ãã«ã¯ãšãªèšèªãå®è£
ããŠããªãããšã瀺ããŠããŸãã ã©ã¡ãã®ã°ã«ãŒãã«ãåªããããŒã¿ããŒã¹è£œåããããŸããããªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹ã¯ããŠãŒã¶ãŒãªã¹ããããã°æçš¿ãªã©ã®æ§é åããŒã¿ãæã€ã¢ããªã±ãŒã·ã§ã³ã«é©ããŠãããšæããŸããæ§é ãå®çŸ©ãããŠããªãããŒã¿ã®æ¹ãè¯ãåŸåã ãã®ã¢ããªã±ãŒã·ã§ã³ã¯ãä»ã®ã»ãšãã©ã®ã¢ããªã±ãŒã·ã§ã³ãšåæ§ã«ãä»»æã®ã¿ã€ãã®ããŒã¿ããŒã¹ã䜿çšããŠå®è£
ã§ããŸãããäžèšã®çç±ããããªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹ã䜿çšããŸãã
第3ç« ã§ã¯ãæåã®Flaskæ¡åŒµæ©èœã玹ä»ããŸããã ãã®ç« ã§ã¯ãããã«2ã€äœ¿çšããŸãã 1ã€ã¯Flask-SQLAlchemyã§ããããã¯ã人æ°ã®SQLAlchemyããã±ãŒãžïŒ Object Relational MapperãŸãã¯ORMïŒã®Flaskãã¬ã³ããªãŒã©ãããŒãæäŸããæ¡åŒµæ©èœã§ãã ORMã䜿çšãããšãã¢ããªã±ãŒã·ã§ã³ã¯ãããŒãã«ãSQLã§ã¯ãªããã¯ã©ã¹ããªããžã§ã¯ããã¡ãœãããªã©ã®é«ã¬ãã«ãªããžã§ã¯ãã䜿çšããŠããŒã¿ããŒã¹ã管çã§ããŸãã ORMã®ç®æšã¯ãé«ã¬ãã«ã®æäœãããŒã¿ããŒã¹ã³ãã³ãã«å€æããããšã§ãã
SQLAlchemyã®æè¯ã®éšåã¯ããã®ORMã¯1ã€ã§ã¯ãªããå€ãã®ãªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹çšã§ããããšã§ãã SQLAlchemyã¯ãäžè¬çãªMySQL ã PostgreSQL ãããã³SQLiteãå«ãããŒã¿ããŒã¹ãšã³ãžã³ã®é·ããªã¹ãããµããŒãããŠããŸãã ãµãŒããŒãå¿
èŠãšããªãåçŽãªSQLiteããŒã¿ããŒã¹ã§éçºãè¡ãããšãã§ãããããããã¯éåžžã«åŒ·åã§ãããããŠãå®çšŒåãµãŒããŒã«ã¢ããªã±ãŒã·ã§ã³ããããã€ãããšããæ¥ããšãã¢ããªã±ãŒã·ã§ã³ãå€æŽããã«ãããä¿¡é Œæ§ã®é«ãMySQLãŸãã¯PostgreSQLãµãŒããŒãéžæã§ããŸãã
Flask-SQLAlchemyãä»®æ³ç°å¢ã«ã€ã³ã¹ããŒã«ããã«ã¯ãå¿
ããããã¢ã¯ãã£ãã«ããŠããå®è¡ããŠãã ããïŒ
(venv) $ pip install flask-sqlalchemy
ããŒã¿ããŒã¹ã®ç§»è¡
ç§ãèŠãããŒã¿ããŒã¹ãã¥ãŒããªã¢ã«ã®ã»ãšãã©ã¯ãããŒã¿ããŒã¹ã®äœæãšäœ¿çšãæ±ã£ãŠããŸãããã¢ããªã±ãŒã·ã§ã³ãå€æŽãŸãã¯å¢å ããå¿
èŠããããããæ¢åã®ããŒã¿ããŒã¹ã®æŽæ°ãäœæããåé¡ãé©åã«è§£æ±ºããŸããã ãªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹ã¯æ§é åãããããŒã¿ã«éäžããŠãããããããã¯å°é£ã§ãããã®ãããæ¢ã«ããŒã¿ããŒã¹ã«ããããŒã¿ã®æ§é ãå€æŽããå Žåã¯ãå€æŽãããæ§é ã«è»¢éããå¿
èŠããããŸãã
ãã®ç« ã§çŽ¹ä»ãã2çªç®ã®æ¡åŒµæ©èœã¯Flask-Migrateã§ã ãããã¯å®éã«ã¯è¬èãªäœ¿çšäººã«ãã£ãŠäœæãããŸãã ãã®æ¡åŒµæ©èœã¯ãSQLAlchemyããŒã¿ããŒã¹ç§»è¡ã®åºç€ã§ããAlembicã®Flaskã©ãããŒã§ãã ããŒã¿ããŒã¹ã®ç§»è¡ã«åãçµããšãæåã¯å°ãæéãããããŸãããå°æ¥çã«ããŒã¿ããŒã¹ã«å€æŽãå ããããã®ä¿¡é Œã§ããæ¹æ³ã«ãéãæãã®ã¯å°é¡ã§ãã
Flask-Migrateã®ã€ã³ã¹ããŒã«ããã»ã¹ã¯ãä»ã®æ¡åŒµæ©èœãšäŒŒãŠããŸãã
(venv) $ pip install flask-migrate
Flask-SQLAlchemyã®æ§æ
éçºäžã«ãSQLiteããŒã¿ããŒã¹ã䜿çšããŸãã SQLiteããŒã¿ããŒã¹ã¯ãåã¢ããªã±ãŒã·ã§ã³ããã£ã¹ã¯äžã®åäžã®ãã¡ã€ã«ã«ä¿åãããMySQLãPostgreSQLãªã©ã®ããŒã¿ããŒã¹ãµãŒããŒãèµ·åããå¿
èŠããªããããå°ããªã¢ããªã±ãŒã·ã§ã³ãéçºããã®ã«æã䟿å©ãªéžæè¢ã§ãã
æ§æãã¡ã€ã«ã«è¿œå ãã2ã€ã®æ°ããæ§æé
ç®ããããŸãã
config.py
import os basedir = os.path.abspath(os.path.dirname(__file__)) class Config(object): # ... SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \ 'sqlite:///' + os.path.join(basedir, 'app.db') SQLALCHEMY_TRACK_MODIFICATIONS = False
Flask-SQLAlchemyæ¡åŒµæ©èœã¯ã SQLALCHEMY_DATABASE_URI
æ§æSQLALCHEMY_DATABASE_URI
ããã¢ããªã±ãŒã·ã§ã³ããŒã¿ããŒã¹ã®å ŽæãSQLALCHEMY_DATABASE_URI
ãŸãã 第3ç« ããèŠããŠããããã«ãç°å¢ãå€æ°ãå®çŸ©ããŠããªãå Žåãç°å¢å€æ°ããæ§æãã»ããã¢ãããããã©ãŒã«ããã¯å€ãæäŸããããšããå§ãããŸãã ãã®å Žåã DATABASE_URL
ç°å¢å€æ°ããããŒã¿ããŒã¹URLãååŸããå®çŸ©ãããŠããªãå Žåã¯ã ããŒã¹ã¢ããªã±ãŒã·ã§ã³å€æ°ã«ããapp.dbãšããããŒã¿ããŒã¹ãæ§æããŸãããã®ããŒã¿ããŒã¹ã¯ã basedir
å€æ°ã«æ ŒçŽãããŠããŸãã
SQLALCHEMY_TRACK_MODIFICATIONS
æ§æSQLALCHEMY_TRACK_MODIFICATIONS
ãFalseã«èšå®ãããšãFlask-SQLAlchemyé¢æ°ãç¡å¹ã«ãªããŸããããã¯ãããŒã¿ããŒã¹ã«å€æŽãå ãããããã³ã«ã¢ããªã±ãŒã·ã§ã³ã«éç¥ããå¿
èŠã¯ãããŸããã
ããŒã¿ããŒã¹ã¯ã ããŒã¿ããŒã¹ã€ã³ã¹ã¿ã³ã¹ãšããŠã¢ããªã±ãŒã·ã§ã³ã«è¡šç€ºãããŸã ã ããŒã¿ããŒã¹ç§»è¡ãšã³ãžã³ã«ãã€ã³ã¹ã¿ã³ã¹ããããŸãã ãããã¯ã app/__ init__.py
ã®ã¢ããªã±ãŒã·ã§ã³ã®åŸã«äœæããå¿
èŠããããªããžã§ã¯ãapp/__ init__.py
ã
from flask import Flask from config import Config from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate app = Flask(__name__) app.config.from_object(Config) db = SQLAlchemy(app) migrate = Migrate(app, db) from app import routes, models
initã¹ã¯ãªããã«3ã€ã®å€æŽãå ããŸããã æåã«ãããŒã¿ããŒã¹ãè¡šãdb
ãªããžã§ã¯ããè¿œå ããŸããã 次ã«ã移è¡ã¡ã«ããºã ãè¡šãå¥ã®ãªããžã§ã¯ããè¿œå ããŸããã Flaskæ¡åŒµæ©èœã®äœ¿çšäŸãã芧ãã ããã ã»ãšãã©ã®æ¡åŒµæ©èœã¯ããã®2ã€ã®ããã«åæåãããŸãã æåŸã«ã以äžã®models
ãšåŒã°ããæ°ããã¢ãžã¥ãŒã«ãã€ã³ããŒãããŸãã ãã®ã¢ãžã¥ãŒã«ã¯ãããŒã¿ããŒã¹ã®æ§é ã決å®ããŸãã
ããŒã¿ããŒã¹ã¢ãã«
ããŒã¿ããŒã¹ã«ä¿åãããããŒã¿ã¯ãäžè¬ã«ããŒã¿ããŒã¹ã¢ãã«ãšåŒã°ããã¯ã©ã¹ã®ã»ããã§è¡šãããŸãã SQLAlchemyã®ORMã¬ãã«ã¯ããããã®ã¯ã©ã¹ããäœæããããªããžã§ã¯ãã察å¿ããããŒã¿ããŒã¹ããŒãã«ã®è¡ã«ãããã³ã°ããããã«å¿
èŠãªå€æãå®è¡ããŸãã
ãŠãŒã¶ãŒãè¡šãã¢ãã«ãäœæããããšããå§ããŸãããã WWW SQL DesignerããŒã«ã䜿çšããŠããŠãŒã¶ãŒããŒãã«ã§äœ¿çšããããŒã¿ãè¡šã次ã®å³ãäœæããŸããã
id
ãã£ãŒã«ãã¯ãã¹ãŠã®ã¢ãã«ã§äžè¬çã«äœ¿çšãããäž»ããŒãšããŠäœ¿çšãããŸãã ããŒã¿ããŒã¹å
ã®åãŠãŒã¶ãŒã«ã¯ããã®ãã£ãŒã«ãã«æ ŒçŽãããŠããäžæã®èå¥åå€ãå²ãåœãŠãããŸãã ã»ãšãã©ã®å Žåãäž»ããŒã¯ããŒã¿ããŒã¹ã«ãã£ãŠèªåçã«å²ãåœãŠããããããäž»ããŒãšããŠããŒã¯ãããid
ãã£ãŒã«ããæå®ããã ãã§ãã
username
ã email
ããã³password_hash
ãã£ãŒã«ãã¯æååïŒãŸãã¯ããŒã¿ããŒã¹çšèªã§ã¯VARCHAR
ãšããŠå®çŸ©ãããããŒã¿ããŒã¹ãã¹ããŒã¹ã®äœ¿çšãæé©åã§ããããã«æ倧é·ãæå®ãããŠããŸãã username
ãšemail
ãã£ãŒã«ãã¯äžç®çç¶ã§ããã password_hash
ãã£ãŒã«ãã¯æ³šç®ã«å€ããŸãã äœæããã¢ããªã±ãŒã·ã§ã³ãæé«ã®ã»ãã¥ãªãã£æšå¥šäºé
ã䜿çšããŠããããšã確èªããããããããŒã¿ããŒã¹ã«ãŠãŒã¶ãŒãã¹ã¯ãŒããä¿åããŸããã ãã¹ã¯ãŒãã®ä¿åã«é¢ããåé¡ã¯ãããŒã¿ããŒã¹ãå±éºã«ããããããšãæ»æè
ããã¹ã¯ãŒãã«ã¢ã¯ã»ã¹ã§ããããã«ãªããããããŠãŒã¶ãŒã«æ害ãäžããå¯èœæ§ãããããšã§ãã ãã¹ã¯ãŒããçŽæ¥èšè¿°ãã代ããã«ãã»ãã¥ãªãã£ã倧å¹
ã«åäžããããã¹ã¯ãŒãããã·ã¥ãèšè¿°ããŸã ã ããã¯å¥ã®ç« ã®ãããã¯ã«ãªãã®ã§ãä»ã¯å¿é
ããªãã§ãã ããã
ãŠãŒã¶ãŒããŒãã«ã«å¿
èŠãªãã®ãããã£ãã®ã§ããããæ°ããapp / models.pyã¢ãžã¥ãŒã«ã®ã³ãŒãã«å€æã§ããŸã ã
from app import db class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), index=True, unique=True) email = db.Column(db.String(120), index=True, unique=True) password_hash = db.Column(db.String(128)) def __repr__(self): return '<User {}>'.format(self.username)
äžèšã§äœæãããUser
ã¯ã©ã¹ã¯ãFlask-SQLAlchemyã®ãã¹ãŠã®ã¢ãã«ã®åºæ¬ã¯ã©ã¹ã§ããdb.Model
ç¶æ¿ããŸãã ãã®ã¯ã©ã¹ã¯ãããã€ãã®ãã£ãŒã«ããã¯ã©ã¹å€æ°ãšããŠå®çŸ©ããŸãã ãã£ãŒã«ãã¯db.Column
ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãšããŠäœæãããŸãããã®ã¯ã©ã¹ã¯ããã£ãŒã«ãã¿ã€ããåŒæ°ãšããŠäœ¿çšããä»ã®ãªãã·ã§ã³ã®åŒæ°ã䜿çšããŸããããšãã°ãäžæã®ã€ã³ããã¯ã¹ä»ããã£ãŒã«ããæå®ã§ããŸããããã¯ãå¹ççãªããŒã¿ããŒã¹æ€çŽ¢ã«éèŠââã§ãã
__repr__
ã¡ãœããã¯ããã®ã¯ã©ã¹ã®ãªããžã§ã¯ããå°å·ããæ¹æ³ãPythonã«æ瀺ããŸããããã¯ãããã°ã«åœ¹ç«ã¡ãŸãã 以äžã®Pythonã€ã³ã¿ãŒããªã¿ãŒã»ãã·ã§ã³ã§ã __repr __()
ã¡ãœããã®åäœã確èªã§ããŸãã
>>> from app.models import User >>> u = User(username='susan', email='susan@example.com') >>> u <User susan>
ãªããžããªã®ç§»è¡ãäœæãã
åã®ã»ã¯ã·ã§ã³ã§äœæãããã¢ãã«ã¯ã©ã¹ã¯ããã®ã¢ããªã±ãŒã·ã§ã³ã®åæããŒã¿ããŒã¹æ§é ïŒãŸãã¯ã¹ããŒã ïŒãå®çŸ©ããŸãã ããããã¢ããªã±ãŒã·ã§ã³ã®æé·ã«äŒŽããæ§é ãå€æŽããå¿
èŠããããŸããããã«ãããæ°ãããšã³ãã£ãã£ãè¿œå ãããå¯èœæ§ããããŸãããèŠçŽ ãå€æŽãŸãã¯åé€ããããšãã§ããŸãã AlembicïŒFlask-Migrateã§äœ¿çšããã移è¡ãã¬ãŒã ã¯ãŒã¯ïŒã¯ãããã®ã¹ããŒããå€æŽãããããããŒã¿ããŒã¹ãæåããåäœæããå¿
èŠã¯ãããŸããã
ãã®äžèŠè€éãªã¿ã¹ã¯ãéæããããã«ãAlembicã¯ç§»è¡ãªããžããªãç¶æããŸããããã¯ã移è¡ã¹ã¯ãªãããä¿åããããã£ã¬ã¯ããªã§ãã ããŒã¿ããŒã¹ã¹ããŒããå€æŽããããã³ã«ãå€æŽã«é¢ãã詳现æ
å ±ãå«ã移è¡ã¹ã¯ãªããããªããžããªã«è¿œå ãããŸãã ããŒã¿ããŒã¹ã«ç§»è¡ãé©çšããããã«ããããã®ç§»è¡ã¹ã¯ãªããã¯äœæãããé ã«å®è¡ãããŸãã
Flask-Migrateã¯ã flask
ã³ãã³ããä»ããŠã³ãã³ããçºè¡ããŸãã Flaskã«ãã€ãã£ããªåŸå±ã³ãã³ãã§ããflask run
ãæ¢ã«èŠãŠããŸããã Flask-Migrateã«ããã flask db
ãµãã³ãã³ããè¿œå ãããããŒã¿ããŒã¹ã®ç§»è¡ã«é¢é£ãããã¹ãŠã管çãããŸãã ããã§ã¯ã flask db init
å®è¡ããŠããã€ã¯ãããã°ã®ç§»è¡ãªããžããªãäœæããŸãããã
(venv) $ flask db init Creating directory /home/miguel/microblog/migrations ... done Creating directory /home/miguel/microblog/migrations/versions ... done Generating /home/miguel/microblog/migrations/alembic.ini ... done Generating /home/miguel/microblog/migrations/env.py ... done Generating /home/miguel/microblog/migrations/README ... done Generating /home/miguel/microblog/migrations/script.py.mako ... done Please edit configuration/connection/logging settings in '/home/miguel/microblog/migrations/alembic.ini' before proceeding.
Flaskã³ãã³ãã¯ãFlaskã¢ããªã±ãŒã·ã§ã³ã®å Žæãç¥ãããã«FLASK_APP
ç°å¢å€æ°ã«äŸåããŠããããšã«FLASK_APP
ããŠFLASK_APP
ã ãã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ã 第1ç« ã§èª¬æãããŠããããã«ã FLASK_APP=microblog.py
ãèšå®ããŸãã
ãã®ã³ãã³ããå®è¡ãããšãæ°ãã移è¡ãã£ã¬ã¯ããªãèŠã€ãããŸãããã®ãã£ã¬ã¯ããªã«ã¯ãããã€ãã®ãã¡ã€ã«ãšããŒãžã§ã³ãµããã£ã¬ã¯ããªããããŸãã ããã§ããããã®ãã¡ã€ã«ã¯ãã¹ãŠãããžã§ã¯ãã®äžéšãšèŠãªãããããŒãžã§ã³ç®¡çã·ã¹ãã ã«è¿œå ããå¿
èŠããããŸãã
æåã®ããŒã¿ããŒã¹ç§»è¡
移è¡ãªããžããªã䜿çšããŠãæåã®ããŒã¿ããŒã¹ç§»è¡ãäœæããŸããããã«ã¯ã Users
ããŒã¿ããŒã¹ã¢ãã«ã«ããããããUsers
ããŒãã«ãå«ãŸããŸãã ããŒã¿ããŒã¹ç§»è¡ãäœæããã«ã¯ãæåãŸãã¯èªåã®2ã€ã®æ¹æ³ããããŸãã èªåçã«ç§»è¡ãäœæããããã«ãAlembicã¯ããŒã¿ããŒã¹ã¢ãã«ã«ãã£ãŠå®çŸ©ãããããŒã¿ããŒã¹ã¹ããŒãããããŒã¿ããŒã¹ã§çŸåšäœ¿çšãããŠããå®éã®ããŒã¿ããŒã¹ã¹ããŒããšæ¯èŒããŸãã 次ã«ãããŒã¿ããŒã¹ã¹ããŒããã¢ããªã±ãŒã·ã§ã³ã¢ãã«ãšäžèŽããããšã確èªããããã«å¿
èŠãªå€æŽã移è¡ã¹ã¯ãªããã«å
¥åããŸãã ãã®å Žåã以åã®ããŒã¿ããŒã¹ããªããããèªå移è¡ã«ããUser
ã¢ãã«å
šäœã移è¡ã¹ã¯ãªããã«è¿œå ãããŸãã flask db migrate
DB transferãµãã³ãã³ãã¯ã次ã®èªå移è¡ãçæããŸãã
(venv) $ flask db migrate -m "users table" INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'user' INFO [alembic.autogenerate.compare] Detected added index 'ix_user_email' on '['email']' INFO [alembic.autogenerate.compare] Detected added index 'ix_user_username' on '['username']' Generating /home/miguel/microblog/migrations/versions/e517276bb1c2_users_table.py ... done
ã³ãã³ãã®åºåãããAlembicã移è¡ã«å«ãŸããŠããããšãããããŸãã æåã®2è¡ã¯åèæ
å ±ã§ãããéåžžã¯ç¡èŠã§ããŸãã ãããã圌ã¯ãããŒãã«'user'
ãš2ã€ã®ã€ã³ããã¯ã¹'['email']'
ããã³ '[' username ']'ãèŠã€ãããšèšããŸãã 次ã«ã移è¡ã¹ã¯ãªãããäœæããå Žæã説æããŸãã e517276bb1c2ã³ãŒãã¯ã移è¡çšã«èªåçæãããäžæã®ã³ãŒãã§ãïŒç°ãªãå ŽåããããŸãïŒã -m
ãªãã·ã§ã³ã§æå®ãããã³ã¡ã³ãã¯ãªãã·ã§ã³ã§ããããã€ãã³ã«çã説æããã¹ããè¿œå ããŸãã
çæããã移è¡ã¹ã¯ãªããã¯ãããžã§ã¯ãã®äžéšã§ãããããŒãžã§ã³ç®¡çã·ã¹ãã ã«å«ããå¿
èŠããããŸãã ã¹ã¯ãªãããã©ã®ããã«èŠãããèå³ãããå Žåã¯ãã¹ã¯ãªããã衚瀺ã§ããŸãã upgrade()
ãšdowngrade()
2ã€ã®æ©èœãããããšãããããŸãã upgrade()
æ©èœã¯ç§»è¡ãé©çšãã downgrade()
æ©èœã¯ç§»è¡ãåé€ããŸãã ããã«ãããAlembicã¯ãããŠã³ã°ã¬ãŒããã¹ã䜿çšããŠãå±¥æŽå
ã®ä»»æã®å ŽæïŒå€ãããŒãžã§ã³ã§ãïŒã«ããŒã¿ããŒã¹ã転éã§ããŸãã
flask db migrate
ã³ãã³ãã¯ããŒã¿ããŒã¹ã«å€æŽãå ããããšã¯ãªããåã«ç§»è¡ã¹ã¯ãªãããäœæããŸãã ããŒã¿ããŒã¹ã«å€æŽãé©çšããã«ã¯ã flask db upgrade
ã³ãã³ãã䜿çšããå¿
èŠããããŸãã
(venv) $ flask db upgrade INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.runtime.migration] Running upgrade -> e517276bb1c2, users table
ãã®ã¢ããªã±ãŒã·ã§ã³ã¯SQLiteã䜿çšããŠãããããUpgradeã³ãã³ãã¯ããŒã¿ããŒã¹ãååšããªãããšãæ€åºããŠäœæããŸãïŒãã®ã³ãã³ãã®å®äºåŸã«app.db
ãšããååã®ãã¡ã€ã«ãã€ãŸãSQLiteããŒã¿ããŒã¹ãè¿œå ãããŸãïŒã MySQLãPostgreSQLãªã©ã®ããŒã¿ããŒã¹ãµãŒããŒã䜿çšããå ŽåãæŽæ°ãéå§ããåã«ããŒã¿ããŒã¹ãµãŒããŒã«ããŒã¿ããŒã¹ãäœæããå¿
èŠããããŸãã
ããŒã¿ããŒã¹ã®ã¢ããã°ã¬ãŒãããã»ã¹ãšãã¢ããã°ã¬ãŒãããã³ããŠã³ã°ã¬ãŒãã®å€æŽã®ããŒã«ããã¯
çŸæç¹ã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã¯ãŸã åæ段éã«ãããŸãããããã¯å°æ¥ã®ããŒã¿ããŒã¹ç§»è¡æŠç¥ã«äœãå«ãŸããããè°è«ããã®ã«æ¯éã¯ãããŸããã éçºãã·ã³ã«ã¢ããªã±ãŒã·ã§ã³ãããããªã³ã©ã€ã³ã§äœ¿çšäžã®æ¬çªãµãŒããŒã«ã³ããŒããããã€ãããŠãããšããŸãã
ã¢ããªã±ãŒã·ã§ã³ã®æ¬¡ã®ããŒãžã§ã³ã§ã¯ãã¢ãã«ã«å€æŽãå ããå¿
èŠããããšããŸããããšãã°ãæ°ããããŒãã«ãè¿œå ããå¿
èŠããããšããŸãã 移è¡ãè¡ããªãå ŽåãããŒã«ã«ãã¹ããšãµãŒããŒã®äž¡æ¹ã§ããŒã¿ããŒã¹ã®ã¹ããŒããå€æŽããæ¹æ³ãç解ããå¿
èŠããããŸãããããã¯å€§ããªåé¡ã«ãªãå¯èœæ§ããããŸãã
ãã ããããŒã¿ããŒã¹ç§»è¡ã®ãµããŒãã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ã¢ãã«ãå€æŽããåŸãæ°ãã移è¡ã¹ã¯ãªããïŒflask db migrateïŒãäœæããŸããèªåäœæãæ£ããããšã確èªããéçºããŒã¿ããŒã¹ïŒflaskïŒã«å€æŽãé©çšããããšã確èªããŸãã db upgradeïŒã 移è¡ã¹ã¯ãªãããããŒãžã§ã³ç®¡çã·ã¹ãã ã«è¿œå ããŠã³ãããããŸãã
æ¬çªãµãŒããŒã§ã¢ããªã±ãŒã·ã§ã³ã®æ°ããããŒãžã§ã³ããªãªãŒã¹ããæºåãã§ããããå¿
èŠãªäœæ¥ã¯ãæ°ãã移è¡ã¹ã¯ãªãããå«ãæŽæ°ãããããŒãžã§ã³ã®ã¢ããªã±ãŒã·ã§ã³ãååŸãã flask db upgrade
ãå®è¡ããããšã ãã§ãã Alembicã¯ãããŒã¿ããŒã¹ãææ°çã«æŽæ°ãããŠããªãããšãæ€åºãã以åã®ãªãªãŒã¹åŸã«äœæããããã¹ãŠã®æ°ãã移è¡ã¹ã¯ãªãããå®è¡ããŸãã
åè¿°ããããã«ãæåŸã®ç§»è¡ããã£ã³ã»ã«ããdowngrade flask db
ã³ãã³ãããããŸãã æ¬çªçšŒåæã«ãã®ãªãã·ã§ã³ãå¿
èŠã«ãªãããšã¯ã»ãšãã©ãããŸããããéçºäžã«éåžžã«åœ¹ç«ã€ããšããããŸãã 移è¡ã¹ã¯ãªãããçæããŠé©çšããã®ã¯ãè¡ã£ãå€æŽãå¿
èŠãªãã®ã§ã¯ãªãããšã確èªããããã ãã§ãã ãã®å ŽåãããŒã¿ããŒã¹ã®è©äŸ¡ãäžãã移è¡ã¹ã¯ãªãããåé€ããŠãããæ°ããã¹ã¯ãªãããäœæããŠçœ®ãæããããšãã§ããŸãã
ããŒã¿ããŒã¹é¢ä¿
ãªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹ã¯ãããŒã¿é
ç®éã®é¢ä¿ãä¿åããã®ã«é©ããŠããŸãã ãŠãŒã¶ãŒãããã°æçš¿ãæžãå ŽåãèããŠãã ããã ãŠãŒã¶ãŒã¯ãŠãŒã¶ãŒããŒãã«ã«ãšã³ããªãæã¡ãã¡ãã»ãŒãžã¯ã¡ãã»ãŒãžããŒãã«ã«ãšã³ããªãæã¡ãŸãã ãã®æçš¿ãæžãã人ãèšé²ããæãå¹æçãªæ¹æ³ã¯ã2ã€ã®èšé²ããªã³ã¯ããããšã§ãã
ãŠãŒã¶ãŒãšæçš¿ã®éã®æ¥ç¶ã確ç«ãããåŸãå¿
èŠã«ãªãå¯èœæ§ã®ãã2çš®é¡ã®èŠæ±ããããŸãã æçš¿ããããã©ã®ãŠãŒã¶ãŒããããæžããããç¥ãå¿
èŠãããå Žåã«æãç°¡åã§ãã ããå°ãè€éãªè³ªåã¯ããã®éã§ãã ãŠãŒã¶ãŒãããå Žåã¯ããã®ãŠãŒã¶ãŒãäœæãããã¹ãŠã®ãšã³ããªãååŸããå¿
èŠããããŸãã Flask-SQLAlchemyã¯ãäž¡æ¹ã®ã¿ã€ãã®ã¯ãšãªã§åœ¹ç«ã¡ãŸãã
æçš¿ãä¿åããããã®ããŒã¹ãæ¡åŒµããŠãå®è¡äžã®æ¥ç¶ã確èªã§ããããã«ããŸãã ãããè¡ãã«ã¯ãããŒã¿ããŒã¹èšèšããŒã«ã«æ»ããã¬ã³ãŒãã®ããŒãã«ãäœæããŸãã
ã¡ãã»ãŒãžããŒãã«ã«ã¯ãå¿
èŠãªèå¥åãã¡ãã»ãŒãžããã¹ããããã³ã¿ã€ã ã¹ã¿ã³ããå«ãŸããŸãã ãããããããã®äºæ³ããããã£ãŒã«ãã«å ããŠãã¡ãã»ãŒãžããã®äœæè
ã«ãã€ã³ãããuser_id
ãã£ãŒã«ããè¿œå ãuser_id
ã ãã¹ãŠã®ãŠãŒã¶ãŒãäžæã®äž»ããŒid
æã£ãŠããããšãããããŸããã ããã°ãšã³ããªãäœæãããŠãŒã¶ãŒã«é¢é£ä»ããæ¹æ³ã¯ããŠãŒã¶ãŒIDãžã®ãªã³ã¯ãè¿œå ããããšuser_id
ãããã¯user_id
ãã£ãŒã«ãuser_id
ãŸã£ããåãuser_id
ã ãã®user_id
ãã£ãŒã«ãã¯å€éšããŒãšåŒã°ããŸãã äžèšã®ããŒã¿ããŒã¹ãã€ã¢ã°ã©ã ã§ã¯ãå€éšããŒã¯ããã£ãŒã«ããšãããåç
§ããããŒãã«ã®id
ãã£ãŒã«ããšã®é¢ä¿ãšããŠè¡šç€ºãããŸãã ã1人ã®ããŠãŒã¶ãŒããå€ãã®ãã¡ãã»ãŒãžãæžã蟌ãããããã®çš®ã®é¢ä¿ã¯1察å€ãšåŒã°ããŸãã
å€æŽãããapp / models.pyã以äžã«ç€ºããŸãïŒ
from datetime import datetime from app import db class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), index=True, unique=True) email = db.Column(db.String(120), index=True, unique=True) password_hash = db.Column(db.String(128)) posts = db.relationship('Post', backref='author', lazy='dynamic') def __repr__(self): return '<User {}>'.format(self.username) class Post(db.Model): id = db.Column(db.Integer, primary_key=True) body = db.Column(db.String(140)) timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) def __repr__(self): return '<Post {}>'.format(self.body)
æ°ããPost
ã¯ã©ã¹ã«ã¯ããŠãŒã¶ãŒãäœæããããã°æçš¿ãå«ãŸããŸãã timestamp
ãã£ãŒã«ãã«ã¯ã€ã³ããã¯ã¹ãä»ããããŸããããã¯ãã¡ãã»ãŒãžãæç³»åã§åä¿¡ããå Žåã«äŸ¿å©ã§ãã ãŸããããã©ã«ãã®åŒæ°ãè¿œå ããé¢æ°datetime.utcnow
ãæž¡ããŸããã ããã©ã«ãé¢æ°ãæž¡ããšãSQLAlchemyã¯ãã®é¢æ°ãåŒã³åºãããã«ãã£ãŒã«ããèšå®ããŸã()
utcnow
åŸã«()
ãutcnow
ãªãã£ããããåŒã³åºãã®çµæã§ã¯ãªãããã®é¢æ°ãèªåã§æž¡ããŸãïŒã éåžžãããã«ããããµãŒããŒã¢ããªã±ãŒã·ã§ã³ã§UTCã®æ¥ä»ãšæå»ãæäœã§ããŸãã ããã«ããããŠãŒã¶ãŒãã©ãã«ããŠãåäžã®ã¿ã€ã ã¹ã¿ã³ãã䜿çšã§ããŸãã ãããã®ã¿ã€ã ã¹ã¿ã³ãã¯ã衚瀺ããããšãã«ãŠãŒã¶ãŒã®çŸå°æéã«å€æãããŸãã
user_id
ãã£ãŒã«ãã¯user.id
å€éšããŒãšããŠåæåãããŸãããã€ãŸãã users
ããŒãã«ã®id
å€ãåç
§ããŸãã ãã®ãªã³ã¯ã§ã¯ãuserã¯ãFlask-SQLAlchemyãå°æåã«å€æãããã¢ãã«ã¯ã©ã¹ã®ååãšããŠèªåçã«èšå®ããããŒã¿ããŒã¹ããŒãã«ã®ååã§ãã Userã¯ã©ã¹ã«ã¯ã db.relationship
ã«ãã£ãŠåæåãããæ°ããã¡ãã»ãŒãžãã£ãŒã«ãããããŸãã ããã¯å®éã®ããŒã¿ããŒã¹ãã£ãŒã«ãã§ã¯ãªãããŠãŒã¶ãŒãšæçš¿ã®é¢ä¿ã®é«ã¬ãã«ã®ãã¥ãŒã§ããããã®ãããããŒã¿ããŒã¹ãã€ã¢ã°ã©ã ã«ã¯ãããŸããã 1察å€ã®é¢ä¿ã®å Žåãdb.relationshipãã£ãŒã«ãã¯éåžžãçåŽã§å®çŸ©ãããå€ãã«ã¢ã¯ã»ã¹ãã䟿å©ãªæ¹æ³ãšããŠäœ¿çšãããŸãã ãããã£ãŠãããšãã°ããŠãŒã¶ãŒãu
ã«æ ŒçŽãããŠããå ŽåãåŒu.posts
ã¯ããã®ãŠãŒã¶ãŒã«ãã£ãŠæžã蟌ãŸãããã¹ãŠã®ã¬ã³ãŒããè¿ãããŒã¿ããŒã¹ã¯ãšãªãããªã¬ãŒããŸãã db.relationship
ã®æåã®åŒæ°ã¯ãå€ãªã¬ãŒã·ã§ã³ã·ããã®ãµã€ããè¡šãã¯ã©ã¹ãæå®ããŸãã backref
åŒæ°ã¯ã1ã€ã®ãªããžã§ã¯ããæãmanyã¯ã©ã¹ã®ãªããžã§ã¯ãã«è¿œå ããããã£ãŒã«ãã®ååãæå®ããŸãã ããã«ãããæçš¿ã®èè
ãè¿ãpost.author
åŒãè¿œå ãããŸãã é
延åŒæ°ã¯ãéä¿¡çšã®ããŒã¿ããŒã¹ã¯ãšãªã®å®è¡æ¹æ³ã決å®ããŸããããã«ã€ããŠã¯åŸã§èª¬æããŸãã ãããã®è©³çŽ°ãæå³ããªããªãå Žåã§ãå¿é
ããªãã§ãã ããããã®èšäºã®æåŸã«äŸã瀺ããŸãã
ã¢ããªã±ãŒã·ã§ã³ã¢ãã«ã®æŽæ°ããããããæ°ããããŒã¿ããŒã¹ç§»è¡ãäœæããå¿
èŠããããŸãã
(venv) $ flask db migrate -m "posts table" INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'post' INFO [alembic.autogenerate.compare] Detected added index 'ix_post_timestamp' on '['timestamp']' Generating /home/miguel/microblog/migrations/versions/780739b227a7_posts_table.py ... done
ãããŠãããŒã¿ããŒã¹ã«ç§»è¡ãé©çšããå¿
èŠããããŸãã
(venv) $ flask db upgrade INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.runtime.migration] Running upgrade e517276bb1c2 -> 780739b227a7, posts table
ãããžã§ã¯ããããŒãžã§ã³ç®¡çã«ä¿åãããŠããå Žåã¯ãæ°ãã移è¡ã¹ã¯ãªãããå¿ããã«è¿œå ããŠãã ããã
æã¡äžãæé
é·ãããŒã¿ããŒã¹äœæããã»ã¹ãå®è¡ãããŸãããããã¹ãŠãã©ã®ããã«æ©èœãããã«ã€ããŠã¯ãŸã 説æããŠããŸããã ã¢ããªã±ãŒã·ã§ã³ã«ã¯ãŸã ããŒã¿ããŒã¹ããžãã¯ããªããããPythonã€ã³ã¿ãŒããªã¿ãŒã§ããŒã¿ããŒã¹ãæäœããŠãããŒã¿ããŒã¹ã«æ
£ããŠã¿ãŸãããã ããã§ã¯ãPythonãå®è¡ããŠãã ããã ã€ã³ã¿ãŒããªã¿ãŒãéå§ããåã«ãä»®æ³ç°å¢ãã¢ã¯ãã£ãã«ãªã£ãŠããããšã確èªããŠãã ããã
Pythonã³ãã³ãã©ã€ã³ã§ãããŒã¿ããŒã¹ã€ã³ã¹ã¿ã³ã¹ãšã¢ãã«ãã€ã³ããŒãããŸãããã
>>> from app import db >>> from app.models import User, Post
:
>>> u = User(username='john', email='john@example.com') >>> db.session.add(u) >>> db.session.commit()
, db.session
. , , db.session.commit()
, . , db.session.rollback()
, . , db.session.commit()
. , .
:
>>> u = User(username='susan', email='susan@example.com') >>> db.session.add(u) >>> db.session.commit()
, :
>>> users = User.query.all() >>> users [<User john>, <User susan>] >>> for u in users: ... print(u.id, u.username) ... 1 john 2 susan
, . â , , all()
. , id
1 2.
. , :
>>> u = User.query.get(1) >>> u <User john>
:
>>> u = User.query.get(1) >>> p = Post(body='my first post!', author=u) >>> db.session.add(p) >>> db.session.commit()
timestamp
, , . user_id? , db.relationship
, User
, posts
, . , , , . SQLAlchemy , .
, :
>>> # get all posts written by a user >>> u = User.query.get(1) >>> u <User john> >>> posts = u.posts.all() >>> posts [<Post my first post!>] >>> # same, but with a user that has no posts >>> u = User.query.get(2) >>> u <User susan> >>> u.posts.all() [] >>> # print post author and body for all posts >>> posts = Post.query.all() >>> for p in posts: ... print(p.id, p.author.username, p.body) ... 1 john my first post! # get all users in reverse alphabetical order >>> User.query.order_by(User.username.desc()).all() [<User susan>, <User john>]
Flask-SQLAlchemy- , , .
, , , :
>>> users = User.query.all() >>> for u in users: ... db.session.delete(u) ... >>> posts = Post.query.all() >>> for p in posts: ... db.session.delete(p) ... >>> db.session.commit()
shell context
, , Python? , , :
>>> from app import db >>> from app.models import User, Post
, Python, . flask shell
â . shell
â "", flask
, . - Python . ããã¯ã©ãããæå³ã§ããïŒ æ¬¡ã®äŸãèããŠã¿ãŸãããã
(venv) $ python >>> app Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'app' is not defined >>> (venv) $ flask shell >>> app <Flask 'app'>
app
, , flask shell
. flask shell
, , , «shell context», .
microblog.py , :
from app import app, db from app.models import User, Post @app.shell_context_processor def make_shell_context(): return {'db': db, 'User': User, 'Post': Post}
app.shell_context_processor
. flask shell
, , . , , , , , , .
, flask shell
, , :
(venv) $ flask shell >>> db <SQLAlchemy engine=sqlite:////Users/migu7781/Documents/dev/flask/microblog2/app.db> >>> User <class 'app.models.User'> >>> Post <class 'app.models.Post'>
<<<å 次>>>