続きです。
SQLAlchemy を使ったDB接続処理と、パッケージ構成の修正をします。
*参考
- Python Flask Tutorial: Full-Featured Web App Part 4 - Database with Flask-SQLAlchemy
- Python Flask Tutorial: Full-Featured Web App Part 5 - Package Structure
*環境
- MacOS
- Python 3.6.3
- Flask 1.0.2
*SQLAlchemy でのDB接続処理
ログインユーザーと投稿したデータを保存できるようにします。データベースは今回は sqlite を使っています。
テーブル定義は class でモデルを作成し、
db.Column()
でカラムを定義します。def __repr__()
を実装しておくと、DBの値を確認するときに値の出力形式を指定できるようになります。<home.py>
from datetime import datetime
from flask import Flask, render_template, url_for, flash, redirect
from flask_sqlalchemy import SQLAlchemy <-- 追加
from forms import RegistrationForm, LoginForm
app = Flask(__name__)
app.config['SECRET_KEY'] = 'cfb33786023cc152019e747a051f73c6'
# ----- 追加ここから -----
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
def __repr__(self):
return "User('{}', '{}', '{}')".format(self.username, self.email, self.image_file)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return "Post('{}', '{}')".format(self.title, self.date_posted)
# ----- 追加ここまで -----
posts = [
{
'author': 'Corey Schafer',
'title': 'Blog Post 1',
'content': 'First post content',
'date_posted': 'April 20, 2018'
},
{
'author': 'Jane Doe',
'title': 'Blog Post 2',
'content': 'Second post content',
'date_posted': 'April 21, 2018'
}
]
@app.route('/')
@app.route('/home')
def home():
return render_template('home.html', posts=posts)
@app.route('/about')
def about():
return render_template('about.html', title='About')
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
# POSTリクエスト(登録時)
flash('Account created for %s!' % form.username.data, 'success')
return redirect(url_for('home'))
# 初期表示時
return render_template('register.html', title='Register', form=form)
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
# POSTリクエスト(ログイン時)
if form.email.data == 'admin@blog.com' and form.password.data == 'password':
flash('You have been logged in!', 'success')
return redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check username and password', 'danger')
# 初期表示時
return render_template('login.html', title='Login', form=form)
if __name__ == '__main__':
app.run(debug=True)
ターミナルから確認をしてみます。
create_all()
をすると定義したテーブルが作成されます。$ python
>>> from home import db
>>> db.create_all()
User テーブルにデータを登録します。
定義したあとに
db.session.add()
をしてデータを追加し、db.session.commit()
で確定します。>>> from home import User, Post
>>> user_1 = User(username='Corey', email='C@demo.com', password='password')
>>> db.session.add(user_1)
>>> user_2 = User(username='JohnDoe', email='jd@demo.com', password='password')
>>> db.session.add(user_2)
>>> db.session.commit()
全て表示するときは
query.all()
、最初の1件はquery.first()
、条件での絞り込みはquery.filter_by()
を使います。この出力ときに
home.py
で定義した__repr__()
の内容が出力されます。また、
drop_all()
をすると全てのデータが削除されます。>>> User.query.all()
[User('Corey', 'C@demo.com', 'default.jpg'), User('JohnDoe', 'jd@demo.com', 'default.jpg')]
>>> User.query.first()
User('Corey', 'C@demo.com', 'default.jpg')
>>> User.query.filter_by(username='Corey').all()
[User('Corey', 'C@demo.com', 'default.jpg')]
>>> User.query.filter_by(username='Corey').first()
User('Corey', 'C@demo.com', 'default.jpg')
*パッケージ構成の修正
home.py
にDB接続やAPI接続処理が混ざっているので、これを機能ごとに別ファイルに切り出します。DB接続処理を
models.py
、API接続処理はroutes.py
に移植します。最終的には下記のディレクトリ構成になります。├── run.py
└── flaskblog
├── __init__.py
├── forms.py
├── models.py
├── routes.py
├── static
└── templates
作業ディレクトリ直下に
flaskblog
フォルダを新規作成します。その配下に__init__.py
を作成して、home.py
の下記設定部分を移植します。__init__.py
に書いてある内容は、別のファイルからimport
して使うことができます。from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SECRET_KEY'] = 'cfb33786023cc152019e747a051f73c6'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
from flaskblog import routes
新規に
models.py
を作成して、home.py
のDB定義の部分を移植します。from datetime import datetime
from flaskblog import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
def __repr__(self):
return "User('{}', '{}', '{}')".format(self.username, self.email, self.image_file)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return "Post('{}', '{}')".format(self.title, self.date_posted)
新規に
routes.py
を作成して、home.py
のAPIの部分を移植します。from flask import render_template, url_for, flash, redirect
from flaskblog import app
from flaskblog.forms import RegistrationForm, LoginForm
from flaskblog.models import User, Post
posts = [
{
'author': 'Corey Schafer',
'title': 'Blog Post 1',
'content': 'First post content',
'date_posted': 'April 20, 2018'
},
{
'author': 'Jane Doe',
'title': 'Blog Post 2',
'content': 'Second post content',
'date_posted': 'April 21, 2018'
}
]
@app.route('/')
@app.route('/home')
def home():
return render_template('home.html', posts=posts)
@app.route('/about')
def about():
return render_template('about.html', title='About')
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
flash('Account created for %s!' % form.username.data, 'success')
return redirect(url_for('home'))
return render_template('register.html', title='Register', form=form)
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
if form.email.data == 'admin@blog.com' and form.password.data == 'password':
flash('You have been logged in!', 'success')
return redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check username and password', 'danger')
return render_template('login.html', title='Login', form=form)
作業ディレクトリ直下に
run.py
を新規作成し、home.py
の下記部分を移植します。from flaskblog import app
if __name__ == '__main__':
app.run(debug=True)
home.py
の内容を別ファイルに全て移植したので、home.py
を削除します。また、ディレクトリ直下に置いていた
static
とtemplate
フォルダをflaskblog
配下に移動します。これでパッケージ構成の修正が完了しました。
*所感
DB接続については、sqlite で十分動作確認はできるので、プロトタイプなど少ないデータで確認を行いたい段階では sqlite だと早く実装できます。パッケージ構成の考え方や別ディレクトリにあるファイルの
import
などの理解ができていなかったのですが、自分で構成を修正してみることで理解が深まりました。Sign up here with your email
ConversionConversion EmoticonEmoticon