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