Django + SQLServer(Azure SQL Database) + SQLAlchemyで、Webアプリを構築
はじめに
この記事は次の2つの要素を同時にやります。
- DjangoのDBにSQLServer(Azure SQL Database)を使う
- DjangoでORMにSQLAlchemyも使えるようにする
作ったもの
Djangoとは?
Djangoはジャンゴと読みます。Dは何処に行った?
DjangoはPythonで最もメジャーなWebアプリケーションフレームワークです。
Djangoはフルスタック・フレームワークなので、DBアクセスとかユーザー認証とか管理画面とか便利な機能が搭載されています。
個人的には、Djangoを使ったことはほとんどありません。以下が理由です。
- 極力フロントエンドとバックエンドを分離したいので、フルスタック・フレームワークを使うことは少ない
- フロントエンドにPythonを使うことは無いし、バックエンドのAPIをPythonで書くとしたらFastAPIを使うことが多い
- データ分析や画像処理やAI以外では、Pythonを積極的には使わない
ですが、最近少し触る機会がありましたので知見を残しておきたいと思います。
この記事のきっかけ
Djangoは、素の状態ではSQLServerに対応していません。
使いたい要件があったので、下記の2案を考えました。
- DjangoのORMをSQLServerに対応させる
- SQLAlchemyなどDjango外のORMを使う
DjangoもSQLServerもSQLAlchemyも初心者すぎて、どちらの案がよいか分からないので、両方を同時にぶち込んで基本的にDjangoで頑張りながら必要な時にSQLAlchemyも使えるようにしようと思いました。
やったこと
その前に、、、
pythonのパッケージ管理にpoetryを使っているので、その前提で書きます。
poetry使わない人は適当にpipとかに置き換えて理解してください。
必要なライブラリのインストール
mssql-django
を追加する
DjangoをSQLServerに対応させる為のライブラリです。
poetry add mssql-django
mssql-django
の補足
「Django SQLServer」とかでググると、django-pyodbc-azure
を入れようと書いてあるサイトが出てきますが、コレは古くて動かないです。古いPythonなら動くかも知れませんが、検証していません。少なくともPython3.8では動きませんでした。
「古い情報しか見つからない」と嘆いている記事の情報も古いという罠。この記事もいずれそうなるのでしょう。
調べてみると、django-pyodbc-azure
→ django-mssql-backend
→ mssql-django
の順にforkされていました。なのでmssql-django
が最新と考えてよいと思いますし、何よりMicrosoft公式に置いてあるライブラリなので一番確実だと思います。
aldjemy
を追加する
DjangoとSQLAlchemyを連携する為のライブラリです。
aldjemyを入れることで、DjangoのModelを使いながら、DB操作にSQLAlchemyも使えるようになります。
poetry add aldjemy
SQLServer用のドライバー(ODBCドライバー)をインストールする
SQLServerを使うのに別途ODBCドライバーが必要なのでinstallします。
OSごとに手順が違うのでMicrosoftのホームページを参照してインストールしてください。
dockerのimage作成時にドライバーがインストールされるように、Dockerfileにインストールコマンドを書いたので、参考に貼っておきます。mssql-django
のドキュメントではドライバのバージョン17が指定されていますが、バージョン18がリリースされているのでそちらを使っています。
Djangoの設定(settings.py)
Djangoの設定ファイルであるsettings.py
に下記のような設定をいれます。
ALDJEMY_ENGINES = {
"mssql": 'mssql+pyodbc'
}
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": os.getenv('MS_DB_NAME', ''),
"USER": os.getenv('MS_DB_USER', ''),
"PASSWORD": os.getenv('MS_DB_PASSWORD', ''),
"HOST": os.getenv('MS_DB_HOST', 'localhost'),
"PORT": os.getenv('MS_DB_PORT', '1433'),
"OPTIONS": {
"driver": os.getenv('MS_DB_DRIVER', 'ODBC Driver 18 for SQL Server'),
"extra_params": os.getenv('MS_DB_EXTRA_PARAMS', ''),
},
},
}
aldjemyの設定
djangoはデフォルトではSQLServerに対応していないので、当然aldjemyもSQLServerに対応していません。
詰んだかと思ったのですが、aldjemyのソースを読んだらALDJEMY_ENGINES
に設定を追加することで動きそうに見えたので、上に貼ったように追加したら動きました。
なぜ、ドキュメントに書いておいてくれないのか。。。
mssql-djangoの設定
DATABASES
を設定すれば動きます。
上に貼ったものは、外から値を投入できるように環境変数を参照するようにしています。
- ENGINE :
mssql
を指定する - NAME : DB名
- USER : DBのユーザー名
- PASSWORD : DBのパスワード
- HOST : DBのホスト
- PORT : DBのポート
- OPTIONS
- driver : ODBCドライバーを指定する文字列(
ODBC Driver 18 for SQL Server
) - extra_params : 追加のパラメーター
- 基本は空でOKだが、ローカルで開発用DBを動かす時は注意(※後で補足します)
- driver : ODBCドライバーを指定する文字列(
DB連携のサンプル
とりあえず、Djangoでモデルを作って、
- Django ORMを使ってPOST
- SQLAlchemyを使ってGET
をそれぞれ書いてみました。
(そうするのがお勧めとかは全く無く、両方使えることを確認するために分けてます。)
Modelを書く
- DjangoでModelを作ります。
DjangoでPOST
- DjangoでFormを作ってPOSTしてます。
エラー処理が雑なのは、サンプルアプリなので許してください。
SQLAlchemyでGET
- aldjemyを入れたことにより、DjangoのModelからSQLAlchemyのModelが取り出せます。
pythonではよくあることですが、aldjemyでは型アノテーションが定義されてなくUser.sa
の型が分からなくて結構困りました。SQLAlchemy上級者なら一目瞭然なのかもしれないですが、初心者には辛いです。。。
- aldjemyを使って、SQLAlchemyのsessionを取得します。
sessionmaker()
が戻り値がSessionの関数
ではなくsessionmakerというクラスのインスタンス生成
であること気がつくのに時間がかかり、ここ苦戦しました。
最後に()
をつけることで、sessionmaker
クラスの__call__
メソッドが呼び出されてSession
が取得出来ます。Pythonの文法難しいな。
- Getします。
session.query(User_sa).all()
上記で、全てのUserのlistがGETできます。
中身がSQLAlchemyの型のためDjangoのViewに放り込んだ時の扱いが面倒くさく感じたので、雑にdictに変換しました。
まとめ
mssql-django
とaldjemy
を使うことで、
Django + SQLServer(Azure SQL Database) + SQLAlchemyの環境が作れる
補足
ローカル環境にてSQLServerをDockerで動かす
開発時は全部ローカルでやりたいことも多いと思うので、一応書いておきます。
むしろこの記事の本編はここかもしれない。
DockerでSQLServerを動かす
SQLServer起動用のcompose.yaml
を書きました。
(最新の仕様に基づき、docker-compose.yml
ではなくcompose.yaml
にしています。)
docker compose up
で起動できます。
SQLServerの起動までで、DBの作成やユーザーの作成は書いていないので、そこは自力でやってください。
SSLエラー対策
開発用にローカルでSQLServerを立ち上げた時は、通常は証明書とか暗号化とかは頑張りたくないと思います。しかし、それだとドライバーの仕様か何かで、セキュリティに引っかかってエラーになってしまいます。
django.db.utils.OperationalError: ('08001', '[08001] [Microsoft][ODBC Driver 18 for SQL Server]TCP Provider: Error code 0x2749 (10057) (SQLDriverConnect)')
これを回避するには、DB設定のextra_params
にTrustServerCertificate=yes
を設定してください。サンプルに作ったアプリは環境変数で設定できるようにしてあります。
これ、世の中の情報が少なくてかなり苦労しました。
DBとアプリの両方を、ローカル環境のDockerで動かすときのネットワーク設定
このサンプルアプリは、アプリ本体起動用のcompose.yaml
も書きました。あると便利なので。
DBとアプリの両方をdocker compose up
で動かそうとすると、別々にdockerを立ち上げることになります。この時ネットワークが分離されるので、アプリからDBを参照できません。ですのでネットワークの設定が追加で必要になります。しかし普段はいらない設定なのでcompose.yaml
に書いてしまうと邪魔になります。
ということで、ローカルのDBに接続する為のネットワーク設定だけを別ファイル分離して書きました。
アプリ本体のcompose.yaml
は、環境変数を.env
から読むようにしています。
.env
にCOMPOSE_FILE
を設定したときのみ追加ファイルも読み込んでネットワーク設定をoverrideできます。
Discussion