2018年7月24日火曜日

Python3 で SQLAlchemy 入門

概要

SQLArchemy は Python で使える ORM です
簡単な CRUD 操作からマイグレーションまで幅広い機能を提供しています
今回は簡単なテーブルの作成から CRUD 操作まで行ってみました

環境

  • macOS X 10.13.6
  • Python 3.6.5
  • SQLAlchemy 1.2.10
  • MySQL Server 5.7.22

インストール

  • pipenv install SQLAlchemy mysqlclient

今回は MySQL にアクセスするので mysqlclient も合わせてインストールします

Tips

おそらく ModuleNotFoundError: No module named 'MySQLdb' のエラーが出ると思います
そして pipenv install MySQL-Python をインストールしようとしたのですがどうやらまだ Python3 に対応していないようです
なので PyMySQL をインストールしようとしたのですが状況は変わらずで最終的に mysqlclient をインストールすることで解決しました

事前準備

  • mysql -u root -p -e "create database test;"

test データベースを作成しておきましょう

とりあえず接続してみる

  • vim test1.py
from sqlalchemy import create_engine

url = 'mysql+mysqldb://user:password@localhost/test?charset=utf8'
engine = create_engine(url, echo=True)
  • pipenv run python3 test1.py

こんな感じです
ユーザ、パスワードの部分は適当に変更してください
test データベースに接続しています

Base クラスを作成して users テーブルの作成準備をする

test1.py にいろいろ追記していきます

  • vim test2.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

url = 'mysql+mysqldb://root@localhost/test?charset=utf8'
engine = create_engine(url, echo=True)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    fullname = Column(String(100))
    password = Column(String(100))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (self.name, self.fullname, self.password)
  • pipenv run python3 test2.py

まず User クラスは Base クラスを継承します
そして __tablename__ 変数でテーブル名を指定します
あとは Column を使ってテーブルに定義するカラムを定義します
__repr__ は必須ではないですが実装しておくとレスポンスをキレイに見せることができます

テーブルを作成する

先ほどのスキーマ定義から実際にテーブルを作成するところまで追加します
と言っても最後の 1 行を追加しているだけです (Base.metadata.create_all(engine))

  • vim test3.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

url = 'mysql+mysqldb://root@localhost/test?charset=utf8'
engine = create_engine(url, echo=True)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    fullname = Column(String(100))
    password = Column(String(100))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (self.name, self.fullname, self.password)

Base.metadata.create_all(engine)
  • pipenv run python3 test3.py

これで test データベースは以下に users というテーブルが作成されています

  • mysql -u user -p password test -e "desc users;"
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| name     | varchar(50)  | YES  |     | NULL    |                |
| fullname | varchar(100) | YES  |     | NULL    |                |
| password | varchar(100) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

sqlalchemy.exc.CompileError: (in table 'users', column 'name'): VARCHAR requires a length on dialect mysql
というエラーになる場合はカラムを定義している String の部分で引数に文字数を指定しているか確認してください
VARCHAR はちゃんと文字数制限を入れないとエラーとなります

データを登録する

engine を元に Session を作成することでテーブルにアクセスすることができます

  • vim test4.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

url = 'mysql+mysqldb://root@localhost/test?charset=utf8'
engine = create_engine(url, echo=True)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    fullname = Column(String(100))
    password = Column(String(100))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (self.name, self.fullname, self.password)

Base.metadata.create_all(engine)

u1= User(name='hawk', fullname='hawksnowlog', password='xxxxxxx')
Session = sessionmaker(bind=engine)
session = Session()
session.add(u1)
session.commit()

最後の session.commit() するまでデータは挿入されません
なのでユーザを複数人登録したり更新処理なども行ってから commit することができます
いわゆるトランザクション処理になります
ちなみに commit する前の状態にロールバックしたい場合は session.rollback() を呼び出せば OK です

データを取得する

  • vim test5.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

url = 'mysql+mysqldb://root@localhost/test?charset=utf8'
engine = create_engine(url, echo=True)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    fullname = Column(String(100))
    password = Column(String(100))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (self.name, self.fullname, self.password)

Base.metadata.create_all(engine)

u1= User(name='hawk', fullname='hawksnowlog', password='xxxxxxx')
Session = sessionmaker(bind=engine)
session = Session()
for i in session.query(User).order_by(User.id):
    print(i.name)

登録する部分を削って取得する部分を足します
いわゆる SELECT 文は session.query() を使います
とりあえず order_by を使っていますがデータは少ないので何でも OK です

SELECT した結果 User クラスの配列を返したい場合は session.query(User).order_by(User.id).all() を呼び出せば OK です

データを削除する

  • vim test6.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

url = 'mysql+mysqldb://root@localhost/test?charset=utf8'
engine = create_engine(url, echo=True)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    fullname = Column(String(100))
    password = Column(String(100))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (self.name, self.fullname, self.password)

Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()
u1 = session.query(User).get(1)
session.delete(u1)
session.commit()

一旦 SELECT してからそのオブジェクトを削除します
あとは commit すれば OK です

データを更新する

削除とほぼ同じです
SELECT してそのオブジェクトの attribute に対してアクセスするだけです

  • vim test7.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

url = 'mysql+mysqldb://root@localhost/test?charset=utf8'
engine = create_engine(url, echo=True)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    fullname = Column(String(100))
    password = Column(String(100))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (self.name, self.fullname, self.password)

Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()
u1 = session.query(User).get(2)
u1.password = 'XXXXXXXX'
session.commit()

最後に commit するのを忘れずに

最後に

Python3 で SQLAlchemy に入門してみました
慣れれば簡単に使えると思います

今回紹介した内容はかなり基本的なことだけです
もっと複雑なクエリの発行や外部キーの制約なども行えます

いろいろなサイトに書いてありましたが一番役に立つのは参考サイトにある公式サイトのチュートリアルなので、これをベースに勉強すると良いと思います
この記事もそのチュートリアルを元に作成しています

参考サイト

0 件のコメント:

コメントを投稿