質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

SQLAlchemy

SQLAlchemyとはPython 用のORMライブラリです。MIT Licenceのオープンソースとして提供されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

1136閲覧

flask + sqlalchemyでクエリストリングスの値をもとにDBデータをUPDATEしたい

cartocchi_0609

総合スコア0

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

SQLAlchemy

SQLAlchemyとはPython 用のORMライブラリです。MIT Licenceのオープンソースとして提供されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2022/04/05 14:39

編集2022/04/09 20:19

プログラミング初心者です。Flaskとsqlalchemyを用いて会社の貸出物品の管理アプリを作ろうと試みています。

実現したいこと

社員が貸出物品を持ち出した記録をDB上に記録
・物品ごとにクエリパラメータを振り、QRコード化
・社員はQRコードをスマホで撮影→持ち出し先と社員名をプルダウン選択し更新ボタンを押下するとDB上の当該データがそれに書き換わる。

・その他の社員が現時点の物品の在庫・所在をアプリ上で確認できる
という状態を目指しています。

app.py

1from flask import Flask,render_template,request,url_for,redirect 2from models.models import SuitoContent 3from models.database import db_session 4from datetime import datetime 5 6app = Flask(__name__) 7 8 9@app.route("/") 10@app.route("/index") 11def index(): 12 13 name = request.args.get("name") 14 num = request.args.get("num") 15 all_suito = SuitoContent.query.all() 16 17 return render_template("index.html", name=name, num=num, all_suito=all_suito) 18 19 20@app.route("/update",methods=['GET','POST']) 21def update(): 22 23 id = request.args.get("id") 24 suito = db_session.query(SuitoContent).filter(SuitoContent.id == id).first() 25 print(suito.date,suito.place,suito.member) 26 suito.date = datetime.now() 27 suito.place = request.form.get("place") 28 suito.member = request.form.get("member") 29 30 db_session.commit() 31 32 return redirect(url_for("index")) 33 34 35 36if __name__ == "__main__": 37 app.run(debug=True)

models.py

1from sqlalchemy import Column, Integer, Text, DateTime 2from models.database import Base 3from datetime import datetime 4 5 6class SuitoContent(Base): 7 __tablename__ = 'suitocontents' 8 id = Column(Integer, primary_key=True) 9 code = Column(Text) #物品の製品番号 10 num = Column(Integer, unique=True) #物品の管理番号 11 name = Column(Text) #物品の製品名 12 place = Column(Text) #所在 13 date = Column(DateTime, default=datetime.now()) 14 member = Column(Text) #最後に動かした社員名 15 16 def __init__(self, code=None, num=None, name=None, place=None, date=None, member=None): 17 self.code = code 18 self.num = num 19 self.name = name 20 self.place = place 21 self.date = date 22 self.member = member 23

database.py

1from sqlalchemy import create_engine 2from sqlalchemy.orm import scoped_session, sessionmaker 3from sqlalchemy.ext.declarative import declarative_base 4import os 5 6databese_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'suito.db') 7engine = create_engine('sqlite:///' + databese_file, convert_unicode=True, connect_args={"check_same_thread": False}) 8db_session = scoped_session(sessionmaker(autocommit=False,autoflush=False,bind=engine)) 9Base = declarative_base() 10Base.query = db_session.query_property() 11 12 13def init_db(): 14 import models.models 15 Base.metadata.create_all(bind=engine)

index.html

1<!DOCTYPE html> 2<html> 3 <head> 4 <title>在庫確認表</title> 5 </head> 6 <body> 7 8 <h1>在庫確認表</h1> 9 {% if num == None %} 10 <h3>現在の在庫状況は下記です。</h3> 11 {% else %} 12 <hr style="border: #ff9900 solid 5px;"> 13 <img src="/static/images/{{num}}.png" alt="画像"> 14 <h3>{{num}}番 {{name}}の在庫情報を変更します。</h3> 15 16 17 <form action="/update" method="post"> 18 <select name="place"> 19 <option hidden>持ち出し先</option> 20 <option value="営業所">営業所</option> 21 <option value="物品1">物品1</option> 22 <option value="物品2">物品2</option> 23 <option value="物品3">物品3</option> 24 <option value="物品4">物品4</option> 25 <option value="物品5">物品5</option> 26 </select> 27 <select name="member"> 28 <option hidden>名前</option> 29 <option value="社員A">社員A</option> 30 <option value="社員B">社員B</option> 31 <option value="社員C">社員C</option> 32 <option value="社員D">社員D</option> 33 <option value="社員E">社員E</option> 34 </select> 35 <input type="submit" name="update" value="更新"> 36 </form> 37 <hr style="border: #ff9900 solid 5px;"> 38 {% endif %} 39 40 41 {% for suito in all_suito %} 42 <table> 43 <tbody> 44 <tr> 45 <td align="center" valign="middle" width="130" style="border: #ff9900 solid 1px; font-size: 100%; padding: 10px;"> 46 <img src="/static/images/{{suito.num}}.png"></td> 47 <td width="400" style="border: #ff9900 solid 1px; font-size: 100%; padding: 10px;"> 48 品名:{{suito.name}} 49 <br>品番:{{suito.code}} | 管理番号:{{suito.num}} 50 <br>所在:{{suito.place}} | 最終更新者:{{suito.member}} 51 <br>最終更新日{{suito.date}} 52 </td> 53 </tr> 54 </tbody> 55 </table> 56 {% endfor %} 57 </body> 58</html>
#QRコード化を想定しているクエリパラメータ例 http://127.0.0.1:5000/index?id=1&num=(管理番号)&name=(製品名)

error

1def update(): 2 Open an interactive python shell in this frame 3 id = request.args.get("id") 4 5 suito = db_session.query(SuitoContent).filter(SuitoContent.id == id).first() 6 suito.place = request.form.get("place") 7 suito.member = request.form.get("member") 8 suito.date = datetime.now() 9 10 db_session.commit() 11 12AttributeError: 'NoneType' object has no attribute 'place'

cmd

1sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 24704 and this is thread id 28128.

調べたところ、スレッド間でのコネクションの使いまわしができないなどと目にしたため、理解できないなりに、

database.py

1engine = create_engine('sqlite:///' + databese_file, convert_unicode=True, connect_args={"check_same_thread": False})

などとしてみましたが、状況は変わらずでした。

なんとか前進させたく、お知恵をお貸しいただけないでしょうか。
よろしくお願い申しげます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

python

1engine = create_engine('sqlite:///' + databese_file, convert_unicode=True, connect_args={"check_same_thread": False})

公式のドキュメントには、以下のように書かれています。
https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#uri-connections

python

1engine = create_engine('sqlite:///' + databese_file + '?check_same_thread=false', convert_unicode=True)

確か、自分も前にこのように対応した覚えがあります。

なおcheck_same_thread=falseは、「チェックをしない」というだけで、「マルチスレッド対応になる」わけではなさそうです。
なので、自分で排他制御を行うべきでしょう。

Pythonドキュメントより引用。

By default, check_same_thread is True and only the creating thread may use the connection. If set False, the returned connection may be shared across multiple threads. When using multiple threads with the same connection writing operations should be serialized by the user to avoid data corruption.


エラーを見落としてた。

AttributeError: 'NoneType' object has no attribute 'place'

このようなエラーが出た場合、スタックトレース、いわゆる「どこでエラーが発生したか」という情報も出力されているはずです。
その情報を元に、エラーメッセージとあわせて自分のソースコード上で「なぜそれが起きるのか」を考えるのがこの手の問題の解決方法になります。

本来ならばどこで起きているか、まではご自身で調べてください、というところですが、今回は特別に…。

エラーメッセージは、「Noneのオブジェクトにはplaceという属性はありません」という意味です。
となると、placeという属性を参照しているところですから、

python

1 suito.place = request.form.get("place")

ではないかと推測されます。(推測なので、あっているかどうかはわかりませんが、その仮定で話を進めると)
となると、placeの属性を参照しているsuitoNoneである為にエラーが起きている、と考えられます。

では、suitoはどこで値が設定されているかといえば、

python

1 suito = db_session.query(SuitoContent).filter(SuitoContent.id == id).first()

ですね。
つまり、firstメソッドがNoneを返しているようです。
であれば、firstメソッドがNoneを返す条件を調べます。

https://www.sukerou.com/2018/12/sqlalchemy-onefirst.html
上記のサイトによれば、条件が0件の場合にNoneを返すようです。
ですので、条件にあったデータがない、と考えられます。
(なんでデータがないか、までは、わかりません)

投稿2022/04/06 00:05

編集2022/04/09 11:19
katsuko

総合スコア3462

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cartocchi_0609

2022/04/08 01:52

katsuko様 早速ご教授いただきありがとうございました。また、お返事が遅れ大変失礼いたしました。 ご指摘点を修正したところ、「SQLite objects created in a thread~」のエラーは出なくなりました。おかげ様で前進でき、ありがとうございます。 公式ドキュメントをしっかり読むよう今後の教訓といたします。 排他制御に関しては、今の自分にはもう少し時間をかけた学習が必要そうです。 「AttributeError: 'NoneType' object has no attribute 'place'」は引き続き出ており、 htmlからFormの値がうまく渡せていない?ような気がしているのですが、こちらもマルチスレッド対応有無と関連しているのでしょうか。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問