SQLAlchemyで取出したdatetime型のデータをJSONに変換するのは、結構面倒みたいなので、marshmallow
を使って簡単に変換します。
SQLAlchemyとmarshmallowは、Flask用のFlask-SQLAlchemy
,Flask-Marshmallow
とmarshmallow-sqlalchemy
があるようですが、今回は使いません。
Flaskと組み合わせるとコードが少し複雑になって、動作が分かりづらくなるので、普通の?SQLAlchemyとmarshmallowを使って動作テストをしていきます。
参考にしたサイトはこちら → https://marshmallow.readthedocs.io/en/stable
もくじ
インストール
pip install sqlalchemy
pip install marshmallow
marshmallow
を使って、JSONに変換します。なぜ、マシュマロって名前なのかよくわかりませんが、ずいぶん可愛らしい名前ですね。マシュマロのスペルをこの時に初めて知りました。
ファイルの階層
.
├── app.py
├── db.py
└── db.sqlite
db.py
SQLAlchemyとsqlite3で簡単なデータベースを作成します。
カラム名 | タイプ |
---|---|
id | Integer |
name | String |
sample1_date | Date |
sample1_datetime | DateTime |
sample2_datetime | DateTime |
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.sqltypes import Integer, String, DateTime, Date
from sqlalchemy.orm import scoped_session, sessionmaker
engine = create_engine('sqlite:///db.sqlite')
Base = declarative_base()
class Sample(Base):
__tablename__ = 'sample'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
sample1_date = Column(Date)
sample1_datetime = Column(DateTime)
sample2_datetime = Column(DateTime)
def __init__(self,name,sample1_date,sample1_datetime,sample2_datetime):
self.name = name
self.sample1_date = sample1_date
self.sample1_datetime = sample1_datetime
self.sample2_datetime = sample2_datetime
Base.metadata.create_all(engine)
session_db = scoped_session(sessionmaker(
autocommit=False,
autoflush=False,
bind=engine
))
Base.query = session_db.query_property()
このデータベースに、こんな感じのデータを2件だけ入力しておきます。
1|dattesar|2022-06-23|2022-03-14 12:00:17.405188|2023-01-21 12:00:17.405188
2|dattesar|2022-07-06|2022-12-28 12:00:18.614196|2022-11-19 12:00:18.614196
app.py
from marshmallow import Schema, fields, pprint
from db import Sample, session_db
class SampleSchema(Schema):
id = fields.Int()
name = fields.Str()
sample1_date = fields.Date()
sample1_datetime = fields.DateTime()
sample2_datetime = fields.DateTime('%Y-%m-%d %H:%M:%S')
sample_data = Sample.query.first()
session_db.close()
pprint(SampleSchema().dump(sample_data))
これで、こんな感じに出力されます。
{'id': 1,
'name': 'dattesar',
'sample1_date': '2022-06-23',
'sample1_datetime': '2022-03-14T12:00:17.405188',
'sample2_datetime': '2023-01-21 12:00:17'}
fields.DateTime()
では、フォーマットを指定しないと、日付と時間の間にTが入るようです。
結構、簡単ですね。ただし、これが上手く動くのは、データベースから取出したデータが1件の時だけです。
Sample.query.all()
でデータを取出すとエラーが出ます。
複数のデータを取出す場合は、many=True
を指定する必要があります。
sample_data = Sample.query.all()
pprint(SampleSchema(many=True).dump(sample_data))
すると、こんな感じに出力されます。
[{'id': 1,
'name': 'dattesar',
'sample1_date': '2022-06-23',
'sample1_datetime': '2022-03-14T12:00:17.405188',
'sample2_datetime': '2023-01-21 12:00:17'},
{'id': 2,
'name': 'dattesar',
'sample1_date': '2022-07-06',
'sample1_datetime': '2022-12-28T12:00:18.614196',
'sample2_datetime': '2022-11-19 12:00:18'}]
しかし、Sample.query.first()
の場合は、many=True
を書くとエラーがでます。
簡単にJSONに変換できると思っていたのに、ちょっと、ややこしくなってきました。
データを検証
ちょっと分からなくなって来たので、検証してみます。
SQLAlchemyは、first()
とall()
で何のタイプを返してくるのでしょうか?
first_data = Sample.query.first()
print(first_data)
print(type(first_data))
print(type(first_data) is list)
all_data = Sample.query.all()
print(all_data)
print(type(all_data))
print(type(all_data) is list)
すると、こんな感じに出力されます。
<db.Sample object at 0x000001F839EDB250>
<class 'db.Sample'>
False
[<db.Sample object at 0x000001F839EDB250>, <db.Sample object at 0x000001F839EDBBE0>]
<class 'list'>
True
first()
ではdb.Sample?が、all()
ではlistが返って来てるのかな?
よくわかりませんが、all()
で返ってくるのがリストなのは間違い無いようです。
ということで、ソースコードをちょっと改良します。
if type(sample_data) is list:
pprint(SampleSchema(many=True).dump(sample_data))
else:
pprint(SampleSchema().dump(sample_data))
これを追記したら、とりあえず、エラーは出なくなり、all()とall()以外、どちらもJSONに変換できるようになりました。
ソースコード
from marshmallow import Schema, fields, pprint
from db import Sample, session_db
class SampleSchema(Schema):
id = fields.Int()
name = fields.Str()
sample1_date = fields.Date()
sample1_datetime = fields.DateTime()
sample2_datetime = fields.DateTime('%Y-%m-%d %H:%M:%S')
sample_data = Sample.query.all()
session_db.close()
if type(sample_data) is list:
pprint(SampleSchema(many=True).dump(sample_data))
else:
pprint(SampleSchema().dump(sample_data))
ちなみに、こんな感じでdatetimeのデータをdateに変換して出力することもできるようです。
sample1_datetime = fields.Date()
{'id': 1,
'name': 'dattesar',
'sample1_date': '2022-06-23',
'sample1_datetime': '2022-03-14',
'sample2_datetime': '2023-01-21 12:00:17'}