2018/09/17

Flask-SQLAlchemyでlast_insert_rowidを取得する

どうも。

Flask-SQLAlchemyとSQLite3で遊んでたらハマってしまったのでそのときのメモです。

目次

野良プログラマー

  • Flask-SQLAlchemy と SQLite を使って遊んでいた
  • SQLiteの last_insert_rowid 関数で最後にINSERTしたROWIDを取得したい
  • でも 0 しか返ってこない
  • add の後 flush する

SQLALCHEMY_ECHOでSQLのログ出したら明らかだった。

INSERTの前に last_insert_rowid を呼び出していた。

Flask==1.0.2
Flask-SQLAlchemy==2.3.2
Flask-WTF==0.14.2

確認できるコードを置いておきます。

$ python -V
Python 3.6.6
$ pip install Flask==1.0.2
$ pip install Flask-WTF==0.14.2
$ pip install Flask-SQLAlchemy==2.3.2
./
├── app.py
├── init.py
├── sample.db
└── templates
    ├── new.html
    └── show.html
  • init.py
# -*- coding: utf-8 -*-
import sqlite3

def main():

    with sqlite3.connect('sample.db') as conn:
        conn.execute("DROP TABLE IF EXISTS users")
        conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT,name CHAR(100) NOT NULL)")
        conn.commit()

if __name__ == '__main__':
    main()
  • init.pyの実行
$ python init.py

templates ディレクトリの配下に show.html と new.html を配置します。

  • show.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>List</title>
  </head>
  <body>
    {% if users %}
    <table>
      {% for user in users %}
      <tr>
        <td>{{ user.id }}</td>
        <td>{{ user.name }}</td>
      </tr>
      {% endfor %}
    </table>
    {% else %}
    <p>ユーザを登録してください。</p>
    {% endif %}
  </body>
</html>
  • new.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>New</title>
  </head>
  <body>
    <p>ユーザ情報を登録してください。</p>
    <form action="{{ url_for('add_user') }}" method="POST">
      {{ form.csrf_token() }}
      <p>名前:{{ form.name }}</p>
      <p>{{ form.submit() }}</p>
    </form>
  </body>
</html>

# db.session.flush()のコメントアウトを外すとlast_insert_rowid()が取得できます。

  • app.py
# -*- coding: utf-8 -*-
import os
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from flask_wtf.csrf import CSRFProtect
from wtforms import validators, StringField, SubmitField

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///./sample.db'
app.config['SQLALCHEMY_ECHO'] = True
app.secret_key = os.urandom(24)
csrf = CSRFProtect(app)
db = SQLAlchemy(app)


class User(db.Model):

    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '<User %r>' % self.name

class AddForm(FlaskForm):

    name = StringField('name', [validators.required(), validators.length(max=100)])
    submit = SubmitField('送信')


@app.route('/user')
def show_user():

    users = User.query.all()
    return render_template('show.html', users=users)

@app.route('/new', methods=['GET','POST'])
def add_user():

    form = AddForm()

    if form.validate_on_submit():

        user = User(name=form.name.data)

        db.session.add(user)
        # db.session.flush()
        id = [row['id'] for row in db.session.execute("SELECT last_insert_rowid() as id")][0]
        print(id)
        db.session.commit()

        return '<p>新しく追加されたユーザのIDは %s です。</p>' % id

    else:
        return render_template('new.html', form=form)

if __name__ == '__main__':
    app.run(host='localhost', port=8080, debug=True)

ユーザの一覧の画面。データがまだない。

ユーザの一覧

ユーザを登録する画面で登録してみる。

ユーザを登録する画面

John Doeと入力した画面

ユーザIDが0と表示された画面

ユーザIDは 1 なんだけどなぁ。

John Doeが追加された画面

で、ログを見てみる。

2018-09-17 14:55:22,706 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-09-17 14:55:22,707 INFO sqlalchemy.engine.base.Engine SELECT last_insert_rowid() as id
2018-09-17 14:55:22,707 INFO sqlalchemy.engine.base.Engine ()
0
2018-09-17 14:55:22,708 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name) VALUES (?)
2018-09-17 14:55:22,708 INFO sqlalchemy.engine.base.Engine ('John Doe',)
2018-09-17 14:55:22,709 INFO sqlalchemy.engine.base.Engine COMMIT
127.0.0.1 - - [17/Sep/2018 14:55:22] "POST /new HTTP/1.1" 200 -

INSERT の前にSELECT last_insert_rowid() as idしてるやん…

で、db.session.flush()を追記した。

(上記のコメントアウトしている# db.session.flush()のコメントを外してね)

もう一回テストしてみる。

名無権兵衛を入力している画面

ユーザIDが2と表示されている画面

名無権兵衛が追加されたユーザ一覧

やったぜ。

ログも確認してみる。

2018-09-17 14:58:59,584 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-09-17 14:58:59,585 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name) VALUES (?)
2018-09-17 14:58:59,585 INFO sqlalchemy.engine.base.Engine ('名無権兵衛',)
2018-09-17 14:58:59,586 INFO sqlalchemy.engine.base.Engine SELECT last_insert_rowid() as id
2018-09-17 14:58:59,586 INFO sqlalchemy.engine.base.Engine ()
2
2018-09-17 14:58:59,587 INFO sqlalchemy.engine.base.Engine COMMIT
127.0.0.1 - - [17/Sep/2018 14:58:59] "POST /new HTTP/1.1" 200 -

INSERT後にSELECT last_insert_rowid()されている。

これでいいのだ。

結構な時間ハマったよ

[広告]

PythonでWebサービスを作る - Python3 + Flaskで作るWebアプリケーション開発入門 - その1

忙しい人のための1日で理解するFlask入門

TVアニメ『けものフレンズ』オリジナルサウンドトラック

関連記事

スポンサーリンク

スポンサーリンク

スポンサーリンク

コメント

非公開コメント