2020年7月14日火曜日

Flask で SQLAlchemy を使ってみた (Flask-SQLAlchemy)

概要

前回 Flask-Migrate でデータベースをマイグレーションしてみました
今回はそのテーブルを使って Flask-SQLAlchemy を使ってデータの CRUD をしてみます

環境

  • macOS 10.15.5
  • MySQL 8.0.19
  • Python 3.8.3
  • Flask-SQLAlchemy 2.4.3

準備

必要なライブラリをインストールします
Flask-SQLAlchemy は MySQL に対して操作するので mysqlclient もインストールします
また MySQL から取得したデータをシリアライズするのに Flask-Marshmallow を使います
marshmallow-sqlalchemy は警告が出るのでインストールしているだけなので直接使うことはありません

  • pipenv install Flask-Marshmallow Flask-SQLAlchemy mysqlclient marshmallow-sqlalchemy

MySQL は事前に起動しておきます

  • brew services start mysql

なお test データベースの user というテーブルにはすでに以下のデータが入っていることを想定しています

mysql> mysql> select * from user;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | hawk |   10 |
|  2 | hawk |   20 |
+----+------+------+
2 rows in set (0.00 sec)

CRUD するモジュールの作成

各種 SQL を発行するモジュールを作成します
追加や削除に関しては db.session を使い取得する場合は User.query を使います
また更新の場合は一度対象のレコードを取り出しモデルにバインドしてから更新したいフィールドを書き換えて再度 db.session.add する感じになります

  • vim lib/__init__.py
from my_app import db, User, UserSchema

class CRUD():
  def add(self, name, age):
      user = User(name=name, age=age)
      db.session.add(user)
      db.session.commit()
      return 'ok'

  def select(self):
      return UserSchema(many=True).dump(User.query.all())

  def _select_one(self, id):
      return User.query.get(id)

  def delete(self, id):
      db.session.delete(self._select_one(id))
      db.session.commit()
      return 'ok'

  def update(self, id, name, age):
      user = self._select_one(id)
      user.name = name
      user.aget = age
      db.session.add(user)
      db.session.commit()
      return 'ok'

今回は 1 つのテーブルに対する操作になりますが複数のテーブルを跨いだリレーションなどを扱う場合ももう少し複雑になります

Flask アプリ側で CRUD をテストするルーティングの追加

Flask アプリの定義やデータベースのマイグレーション処理も含まれているので少し長いです

  • my_app/__init__.py
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask_migrate import Migrate

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqldb://{}:{}@{}/{}?charset=utf8".format("root", "", "localhost", "test")
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

db = SQLAlchemy(app)
migrate = Migrate(app, db)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))
    age = db.Column(db.Integer)

ma = Marshmallow(app)
class UserSchema(ma.Schema):
    class Meta:
        fields = ("id", "name", "age")

@app.route('/add')
def add_user():
    name = request.args.get('name')
    age = request.args.get('age')
    crud = CRUD()
    return crud.add(name, age)

@app.route('/select')
def select_user():
    crud = CRUD()
    return jsonify(crud.select())

@app.route('/delete')
def delete_user():
    id = request.args.get('id')
    crud = CRUD()
    return crud.delete(id)

@app.route('/update')
def update_user():
    id = request.args.get('id')
    name = request.args.get('name')
    age = request.args.get('age')
    crud = CRUD()
    return crud.update(id, name, age)

from my_app.lib import CRUD # 循環参照になるので最後に import する

route に関しては各 CRUD 処理分定義しています
クエリストリングにしていますが好きな形式でリクエストを受け取れるようにして OK です
UserSchema クラスは SQLAlchemy で取得したデータを json にシリアライズするために追加したクラスです
あとは前回マイグレーションで作成した処理になります

動作確認

では動作確認しましょう
まずはアプリを起動します

  • FLASK_APP=my_app pipenv run flask run

アプリが起動したら API を叩いてみましょう

  • curl 'localhost:5000/add?name=test&age=30'
  • curl 'localhost:5000/select'
  • curl 'localhost:5000/delete?id=3'
  • curl 'localhost:5000/update?id=2&name=snowlog&age=99'

最後に

Flask-SQLAlchemy を使って Flask から MySQL に対して簡単な CRUD 処理を行ってみました
エラーハンドリングなど全くしていないのでプロダクトではちゃんと行うようにしてください
正規化をしまくっているデータベースだともっと複雑なクエリを発行することになると思いますが基本的な使い方は理解できるかなと思います

ちょっと書き方的に気になるのが Flask アプリ内にデータベースのモデルを追加しているところです
Flask-Migrate を使う上でそうしていますがもう少し工夫すれば別モジュールとして管理できるかもしれません

参考サイト

0 件のコメント:

コメントを投稿