昨日(5月9日)づけでリリースされていた。
ISO イメージをダウンロードした。時間ができたらクリーンインストールしよう。
takatoh's blog – Learning programming languages.
昨日(5月9日)づけでリリースされていた。
ISO イメージをダウンロードした。時間ができたらクリーンインストールしよう。
これまで、docker
コマンドを sudo
付きで実行してきた。そうしないと、パーミッションがない、と怒られるからだけど、面倒なので一般ユーザで実行できないか調べてみた。
こたえ: ユーザを docker グループに追加すればいい
というわけで、つぎのようにした。
takatoh@apostrophe $ sudo usermod -a -G docker takatoh
これで sudo
をつけずとも実行できるようになった(↓のように)。
takatoh@apostrophe $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE b latest 4e366dc7de8f 5 hours ago 451MB bruschetta 1 197d15517e40 5 hours ago 451MB ubuntu 20.04 1d622ef86b13 2 weeks ago 73.9MB
今日は練習。
題材は自作の書籍管理webアプリ(GitHub)。これを Docker コンテナで動かすことを目指す。
あと、これまで CentOS を使ってきたけど、ふだんメインに使っているのが Ubuntu なのと、先月リリースされた 20.04 LTS が使える(Docker Hub にイメージがある)ようなので、これをベースにしてみる。
さて、一応やり方を覚えたとはいえ、いきなり Dockerfile をイチからかけるわけではないので、Ubuntu:20.04 のイメージからコンテナを起動して、必要なソフトのインストール、設定ファイルのコピーなど手作業でやりながら Dockerfile を書き起こす、というやり方をした。
書き上がった Dockerfile はこう:
FROM ubuntu:20.04 RUN apt update RUN apt install -y python3 python3-pip git RUN pip3 install uwsgi RUN git clone https://github.com/takatoh/Bruschetta.git /usr/bruschetta RUN cd /usr/bruschetta && pip3 install -r requirements.txt RUN cd /usr/bruschetta && python3 manage.py init_db COPY files/bruschetta.ini /usr/bruschetta/ CMD [ "/usr/local/bin/uwsgi", "/usr/bruschetta/bruschetta.ini" ]
ファイル構成:
takatoh@apostrophe $ tree . . ├── Dockerfile └── files └── bruschetta.ini 1 directory, 2 files
web アプリは Python 製なので、uWSGI で動かす。./files/bruschetta.ini はその設定ファイル。
[uwsgi] http = :5000 chdir = /usr/bruschetta wsgi-file = /usr/bruschetta/manage.py callable = app master = true pidfile = /usr/bruschetta/bruschetta.pid daemonize = /usr/bruschetta/bruschetta.log
これで準備は OK。Docker イメージをビルドする。
takatoh@apostrophe $ sudo docker build -t bruschetta:1 . (略) Successfully built 197d15517e40 Successfully tagged bruschetta:1
うまくいったようだ。
takatoh@apostrophe $ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE bruschetta 1 197d15517e40 3 minutes ago 451MB ubuntu 20.04 1d622ef86b13 2 weeks ago 73.9MB
では、いま作ったイメージ bruschetta:1 を起動。
takatoh@apostrophe $ sudo docker run -it -d -p 8080:5000 --name bruschetta-1 bruschetta:1
これで、ブラウザで localhost:8080 にアクセスすれば web アプリに繋がる……はずなんだけど、つながらない。
コンテナの状況を見てみると:
takatoh@apostrophe $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1dc9e16f813a bruschetta:1 "/usr/local/bin/uwsg…" 5 seconds ago Exited (0) 4 seconds ago bruschetta-1
STATUS が Exited になっている。どういうこと?
何が起きているのかを探るために、やり方をググるとつぎのページを見つけた。
このページに従ってまずはコンテナのログを見てみる。
takatoh@apostrophe $ sudo docker logs bruschetta-1 [uWSGI] getting INI configuration from /usr/bruschetta/bruschetta.ini
うーん、ちゃんと uWSGI が起動した(ということはつまり web アプリが起動した)ように見える。
じゃあつぎ、コンテナからイメージを作って、そのイメージからコンテナを起動し、中に入って様子を見る。コンテナからイメージを作るのは docker commit
コマンド。
takatoh@apostrophe $ sudo docker commit bruschetta-1 b
一時的な用途のイメージなので b というテキトーな名前をつけている。これをつぎのように起動する。
takatoh@apostrophe $ sudo docker run --rm -it -p 8080:5000 b bash
これでコンテナの中には入れたので web アプリを起動してみる。
root@2ab03bb28bc5:/# /usr/local/bin/uwsgi /usr/bruschetta/bruschetta.ini [uWSGI] getting INI configuration from /usr/bruschetta/bruschetta.ini
この結果どうなったかというと……期待通りに web アプリが動いた!
えぇ!?どういうこと?
CentOS 7 のイメージをもとにして作っても、同じ現象に遭遇した。
昨日は手作業で構成した Docker コンテナから Docker イメージを作った。
だけど、この方法だと同じイメージを再度作るにも手作業を行わなければならない。全く同じイメージでいいならイメージを使い回せばいいけど、ちょっと何かを付け足したいとしたら?あるいは構成するソフトウェアをバージョンアップしたいとしたら?
Docker にはイメージを構成する手順を既述したファイル(Dockerfile という名前)を使ってイメージをビルドする機能がある。Dockerfile はテキストファイルなので、変更するのも簡単だし、何より Git 等で管理することができる。これは OS 設定などを含めたインフラ構築をコード化できることを意味していて、「Infrastructure as Code (IaC)」と呼ぶ。
今日は Dockerfile を記述して、それをもとに Docker イメージを作り、コンテナを起動するまでを試してみる。
昨日と同じ、CentOS 上で Tomcat が動作するコンテナを作る。
つぎのページを参考にした。
また、混乱しないように、昨日までに作ったイメージとコンテナは全て削除した。
つぎの通り:
takatoh@apostrophe $ tree . . ├── Dockerfile └── files └── apache-tomcat-9.0.34.tar.gz
Dockerfile のほかに ./files/apache-tomcat-9.0.34.tar.gz があるのは、コンテナの外(ホスト側)からコピーしたいから。コンテナの中でダウンロードしてもいいのかもしれないけど、昨日までの例に従って、予めダウンロードしておいたファイルをコピーする。
で、Dockerfile はつぎのように記述した。
FROM centos:7 RUN yum install -y java ADD files/apache-tomcat-9.0.34.tar.gz /opt/ CMD [ "/opt/apache-tomcat-9.0.34/bin/catalina.sh", "run" ]
4行ある。各行は <命令> <引数> ...
となっていて、Docker はこれを1行ずつ実行してイメージを構成する。順に見ていこう。
準備ができたので Docker イメージを作ろう。docker build
コマンドを使う。
takatoh@apostrophe $ sudo docker build -t tomcat:1 .
-t
オプションはイメージ名の指定、引数の .
は Dockerfile のあるディレクトリを指定している。
これでイメージができた。
takatoh@apostrophe $ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE tomcat 1 6f24f0e76833 About a minute ago 477MB centos 7 b5b4d78bc90c 2 days ago 203MB
tomcat というイメージがいま作ったもの。centos はベースとするために Docker Hub から撮ってきたものだ。
最後に、ちゃんとコンテナが起動するか、確認しよう。
takatoh@apostrophe $ sudo docker run -it -d --name tomcat-1 -p 18081:8080 tomcat:1
ブラウザで localhost:18081 にアクセスすると、期待通り Tomcat が動いているのを確認できた。
昨日は Docker Hub から取ってきた CentOS のイメージをもとにしてコンテナを起動し、Tomcat をインストールして動かしてみた。
今日は、そのコンテナから Tomcat インストール済みのイメージを作ってみる。
まずは現状の確認。イメージから。
takatoh@apostrophe $ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE centos 7 b5b4d78bc90c 39 hours ago 203MB
centos という名前のイメージがある。これは昨日 Docker Hub から取ってきたもの。
takatoh@apostrophe $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2a1533db2a0e centos:7 "/bin/bash" 31 hours ago Exited (137) 31 hours ago tomcat
コンテナは tomcat という名前で残っている(停止中)。これは centos イメージから作ったものだ。
コンテナからイメージを作るには docker commit
コマンドを使う。今回は tomcat コンテナから tomcat-image という名前のイメージを作った。
takatoh@apostrophe $ sudo docker commit tomcat tomcat-image
イメージのリストを見ると、いま作ったイメージの名前が見える。
takatoh@apostrophe $ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE tomcat-image latest fa94a527ab79 24 seconds ago 488MB centos 7 b5b4d78bc90c 39 hours ago 203MB
それじゃ、作ったばかりの tomcat-image からコンテナを起動してみよう。その前にログ用のディレクトリを作っておく。
takatoh@apostrophe $ sudo mkdir /var/log/tomcat-container2
で、コンテナ起動。コンテナ名は昨日のと被らないように tomcat2 とした。ホスト側のポートも 18082 にした。
takatoh@apostrophe $ sudo docker run -it -d -p 18082:8080 -v /var/log/tomcat-container2:/share/logs --name tomcat2 tomcat-image
コンテナの確認。起動した tomcat2 という名前のコンテナがあり、元になったイメージが tomcat-image であることがわかる。
takatoh@apostrophe $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e2735abcaf58 tomcat-image "/bin/bash" 28 seconds ago Up 26 seconds 0.0.0.0:18082->8080/tcp tomcat2 2a1533db2a0e centos:7 "/bin/bash" 31 hours ago Exited (137) 31 hours ago tomcat
コンテナの中に入って Tomcat を起動。
takatoh@apostrophe $ sudo docker exec -it tomcat2 bash
[root@e2735abcaf58 /]# cd /opt/apache-tomcat-9.0.34 [root@e2735abcaf58 apache-tomcat-9.0.34]# ./bin/startup.sh
ホスト側のブラウザで localhost:18082 にアクセスすると、ちゃんと Tomcat が動いているのが確認できた。
今日はここまで。
Tomcat を題材にして、Docker コンテナとのやり取りを試してみる。つぎのページを参考にした。
Docker Hub から CentOS7 のイメージを取ってくる。
takatoh@apostrophe $ sudo docker pull centos:7
後で使うので /var/log/tomcat-container ディレクトリを作っておく。
takatoh@apostrophe $ sudo mkdir /var/log/tomcat-container
コンテナを起動。-v
オプションは、ホストとコンテナのディレクトリ共有のオプション(後述)。
takatoh@apostrophe $ sudo docker run -it -d -p 18080:8080 -v /var/log/tomcat-container:/share/logs --name tomcat centos:7
これで CentOS のコンテナが起動した。っていうか、Ubuntu 上で CentOS のコンテナを動かせるんだね。
公式サイトから Apache Tomcat をダウンロード。
takatoh@apostrophe $ wget https://downloads.apache.org/tomcat/tomcat-9/v9.0.34/bin/apache-tomcat-9.0.34.tar.gz
ダウンロードしたファイルをコンテナ内にコピー。
takatoh@apostrophe $ sudo docker cp apache-tomcat-9.0.34.tar.gz tomcat:/opt/
起動中のコンテナの中に入るには、docker exec
コマンド。シェルに bash を指定している。
takatoh@apostrophe $ sudo docker exec -it tomcat bash
コンテナ内にて、まずは Java をインストール。
[root@f656baaeb4b8 /]# yum install -y java
つづいて Tomcat のインストール。というか、これはアーカイブファイルを展開するだけ。で、Tomcat を起動する。
[root@f656baaeb4b8 /]# cd /opt [root@f656baaeb4b8 opt]# ls apache-tomcat-9.0.34.tar.gz [root@f656baaeb4b8 opt]# tar zxf apache-tomcat-9.0.34.tar.gz [root@f656baaeb4b8 opt]# cd apache-tomcat-9.0.34 [root@f656baaeb4b8 apache-tomcat-9.0.34]# ./bin/startup.sh
これで OK。ホスト側のブラウザで http://localhost:18080/ にアクセスすると、ちゃんと Tomcat が動いているのが確認できた。
さて、Tomcat のログはコンテナの中にある。コンテナを停止したらログを見るためにはもう一度コンテナを起動する必要がある。さらに、コンテナを削除してしまったら、もうログを見る術はない。
ここで、ホストとコンテナのディレクトリ共有が生きてくる。docker run
コマンドで指定した -v
オプションだ。コンテナを起動するときに -v /var/log/tomcat-container:/share/logs
と指定した。これでホストの /var/log/tomcat-container とコンテナの /share/logs が共有される。
いま、Tomcat のログファイルは、インストールしたディレクトリの下の logs ディレクトリに書きだされている(これが Tomcat のデフォルト)。そこでログファイルの出力先を /share/logs に変更する。
Tomcat をいったん停めて、設定ファイルを書き換えて、再起動。
[root@2a1533db2a0e apache-tomcat-9.0.34]# ./bin/shutdown.sh [root@2a1533db2a0e apache-tomcat-9.0.34]# sed -i -e "s/\${catalina.base}\/logs/\/share\/logs/g" ./conf/logging.properties [root@2a1533db2a0e apache-tomcat-9.0.34]# ./bin/startup.sh
これで、/share/logs ディレクトリにログファイルが出力されるようになった。
[root@2a1533db2a0e apache-tomcat-9.0.34]# ls -la /share/logs total 20 drwxr-xr-x 2 root root 4096 May 6 05:04 . drwxr-xr-x 3 root root 4096 May 6 04:59 .. -rw-r----- 1 root root 5883 May 6 05:04 catalina.2020-05-06.log -rw-r----- 1 root root 0 May 6 05:04 host-manager.2020-05-06.log -rw-r----- 1 root root 408 May 6 05:04 localhost.2020-05-06.log -rw-r----- 1 root root 0 May 6 05:04 manager.2020-05-06.log
ディレクトリ共有しているホストの /var/log/tomcat-container にも同じログファイルがあるのがわかる。
takatoh@apostrophe $ ls -la /var/log/tomcat-container 合計 20 drwxr-xr-x 2 root root 4096 5月 6 14:04 . drwxrwxr-x 14 root syslog 4096 5月 6 13:19 .. -rw-r----- 1 root root 5883 5月 6 14:04 catalina.2020-05-06.log -rw-r----- 1 root root 0 5月 6 14:04 host-manager.2020-05-06.log -rw-r----- 1 root root 408 5月 6 14:04 localhost.2020-05-06.log -rw-r----- 1 root root 0 5月 6 14:04 manager.2020-05-06.log
これらはコンテナを停止しても見ることができる。
takatoh@apostrophe $ sudo docker stop tomcat takatoh@apostrophe $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2a1533db2a0e centos:7 "/bin/bash" 12 minutes ago Exited (137) 9 seconds ago tomcat
takatoh@apostrophe $ ls -la /var/log/tomcat-container 合計 20 drwxr-xr-x 2 root root 4096 5月 6 14:04 . drwxrwxr-x 14 root syslog 4096 5月 6 13:19 .. -rw-r----- 1 root root 5883 5月 6 14:04 catalina.2020-05-06.log -rw-r----- 1 root root 0 5月 6 14:04 host-manager.2020-05-06.log -rw-r----- 1 root root 408 5月 6 14:04 localhost.2020-05-06.log -rw-r----- 1 root root 0 5月 6 14:04 manager.2020-05-06.log
とりあえずここまで。
コンテナの状態を確認。
takatoh@apostrophe $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8f048cac2336 hello-world "/hello" 2 days ago Exited (0) 2 days ago vigilant_varahamihira
一昨日のエントリで走らせた hello-world のコンテナが残っている。vigilant_varahamihira っていう名前になってるけど、これは Docker かテキトーにつけた名前だろう。
イメージの確認は docker image ls
。
takatoh@apostrophe $ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest bf756fb1ae65 4 months ago 13.3kB
docker iamges
でも同じ結果が得られる。
takatoh@apostrophe $ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest bf756fb1ae65 4 months ago 13.3kB
Docker Hub からイメージを取得して、コンテナ起動してみる。
イメージの取得は docker pull
。
takatoh@apostrophe $ sudo docker pull nginx
これで nginx イメージの取得ができた。確認してみる。
takatoh@apostrophe $ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 602e111c06b6 11 days ago 127MB hello-world latest bf756fb1ae65 4 months ago 13.3kB
コンテナの起動は docker run
。
takatoh@apostrophe $ sudo docker run -d --name nginx-container -p 8181:80 nginx
オプションの意味はつぎの通り:
-d
コンテナをバックグラウンドで起動--name
コンテナ名を指定-p
ホストとコンテナ間のポートフォワード設定。-p <ホスト側ポート>:<コンテナ側ポート>コンテナの状態を確認すると、nginx-container という名前で起動している(STATUS が Up になっている)のがわかる。
takatoh@apostrophe $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fc4887604124 nginx "nginx -g 'daemon of…" 4 minutes ago Up 4 minutes 0.0.0.0:8181->80/tcp nginx-container 8f048cac2336 hello-world "/hello" 2 days ago Exited (0) 2 days ago vigilant_varahamihira
ブラウザで http://localhost:8181/ にアクセスすると Nginx のデフォルトページが表示された。
コンテナの停止は docker stop
。
takatoh@apostrophe $ sudo docker stop nginx-container
コンテナの状態を確認すると、STATUS が Exited になっている。
takatoh@apostrophe $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fc4887604124 nginx "nginx -g 'daemon of…" 8 minutes ago Exited (0) About a minute ago nginx-container 8f048cac2336 hello-world "/hello" 2 days ago Exited (0) 2 days ago vigilant_varahamihira
コンテナを削除するには docker container rm
。
takatoh@apostrophe $ sudo docker container rm nginx-container
イメージを削除するには docker image rm
。
takatoh@apostrophe $ sudo docker image rm nginx
takatoh@apostrophe $ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest bf756fb1ae65 4 months ago 13.3kB
nginx イメージが削除されている。
公式サイトのドキュメントを参考にした。
まずは古いバージョンの Docker Engine をアンインストールせよ、とのことだけど、今回ははじめてインストールするのでこの作業はパス。
なので最初にするのは apt-get update
。
takatoh@apostrophe $ sudo apt-get update
必要なパッケージをインストール。
takatoh@apostrophe $ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
すでに全部インストールされていたようだ。
つぎに Docker 公式の GPG key をインストール。
takatoh@apostrophe $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - OK
ちゃんとインストールできたか確認。
takatoh@apostrophe $ sudo apt-key fingerprint 0EBFCD88 pub 4096R/0EBFCD88 2017-02-22 フィンガー・プリント = 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 uid Docker Release (CE deb) [email protected] sub 4096R/F273FCD8 2017-02-22
なんかドキュメントの例と出力が違うけど、日付とフィンガー・プリントが合ってるから大丈夫なんだろう。
リポジトリを登録。
takatoh@apostrophe $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
途中に出てくる lsb_release -cs
コマンドは Ubuntu ディストリビューションの名前を返す。↓こんな感じ。
takatoh@apostrophe $ lsb_release -cs xenial
takatoh@apostrophe $ sudo apt-get update takatoh@apostrophe $ sudo apt-get install docker-ce docker-ce-cli containerd.io
最後に hello-world
イメージを走らせてみて、ちゃんとインストールできてるかを確認。
takatoh@apostrophe $ sudo docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 0e03bdcc26d7: Pull complete Digest: sha256:8e3114318a995a1ee497790535e7b88365222a21771ae7e53687ad76563e8e76 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/
大丈夫そうだ。
このブログじゃなくて、もうひとつ別に運用しているサイト(ブログ)を引越した。
まずは、引越し先のレンタルサーバにドメインを結びつけてから、WordPress をインストール。さくらのレンタルサーバでは、クイックインストールという機能が用意されているので簡単だ。
引越し元・先の両方の WordPress に All-in-One WP Migration プラグインをインストールして有効化。
ついでに、引越し先には All-in-One WP Migration File Extension プラグインもインストールしておく。このプラグインはインポートできる最大ファイルサイズを大きくするもの。All-in-One WP Migration プラグインだけでは最大 5MB のところ、512MB になる。
インストールするには、https://import.wp-migration.com からプラグイン(zipファイル)をダウンロードして、WordPress の管理画面でファイルからインストールすればいい。
引越し元の WordPress の管理画面左のメニューに「All-in-One WP Migration」があるので、「エクスポート」を選ぶ。細かいオプションもあるけど、今回は全てデフォルト。エクスポート先に「ファイル」を選んで少し待つとダウンロードできるようになるので、ダウンロードしておく。ファイルは .wpress という拡張子の独自形式のようだ。
ちなみに、今回ダウンロードしたファイルは 60MB あった。だから All-in-One WP Migration File Extension が必要なわけ。
今度は引越し先の WordPress で、「All-in-One WP Migration」→「インポート」で上でダウンロードしたファイルを指定する。確認のダイアログが出て、「開始」をクリック。しばらくすると完了。
いまのところ、引越し元のブログも残っていて、新しい方は別のドメイン名(サブドメイン)で動いている。しばらくはこのまま様子見。
引越し元のレンタルサーバは6月初めに契約が切れる(なにもしないと自動更新)ので、そのタイミングで完全に切り替えようかと思っている。
うまい例をでっち上げられないので、実際のユースケースで説明する。
自宅のサーバで Flask を使った書籍管理の web アプリを運用してるんだけど(ソースコードは GitHub で公開してある)、その web アプリには JSON を返す API が定義してある。で、API のひとつ /api/books/ は書籍情報の一覧を返し、クエリパラメータとして offset と limit を取ることができる。
Flask では request.args.get でクエリパラメータの値を取得するついでに、値の型とデフォルト値を指定することができる。↓こんな感じ。
@app.route('/api/books/') def api_books(): offset = request.args.get('offset', default=0, type=int) limit = request.args.get('limit', default=100, type=int) ....
さて、この書籍管理アプリには「書籍を削除する」という機能がなくて、かわりに書籍を捨てた時には、データベース上の disposed カラムに True をセットして、web アプリ上では表示しないようになっている。だけど上記の API ではそこのところを考慮してなかったので、返ってくる JSON には捨てた書籍も含まれている、という状況だった。
ここからが本題。
API の返す JSON に、デフォルトでは捨てた書籍(つまり disposed=True)は含まず、クエリで include_disposed に真値をセットした時にだけ含むようにしたい。
上に示したコートでは、クエリパラメータの値を int に変換して取得しているんだから、同様に bool を指定してやればいいと思った。こういうふうに。
@app.route('/api/books/') def api_books(): offset = request.args.get('offset', default=0, type=int) limit = request.args.get('limit', default=100, type=int) include_disposed = request.args.get('include_disposed', default=False, type=bool) ....
開発用のサーバはエラーもなく立ち上がり、/aip/books/ にアクセスすると捨てた書籍が含まれていない JSON が、/api/books/?include_disposed=True にアクセスすると捨てた書籍も含まれた JSON が返ってきた。期待通りだ。
ところが、試しに /api/books/?include_disposed=False にアクセスしてみると、捨てた書籍も含まれた JSON が返ってきた。これは期待する動作と違う。
原因は追求していないけど、想像するに、クエリの値として渡ってきた文字列を bool(文字列) してるだけなんじゃなかろうか。だとすると、クエリで include_disposed の値がなんであるかにかかわらず、空文字列でない限りは、変数 include_disposed の値は True になるわけだ。
なんてこった。期待させやがって!
しかたがないので文字列を真偽値に変換する関数を書いた。一般に真を表すであろう文字列(true、 yes、 on、大文字小文字を問わない)の時だけ True を返し、ほかは False を返す。
def str_to_bool(s): p = re.compile(r'(true|yes|on)\Z', re.IGNORECASE) if p.match(s): return True else: return False
で、クエリの値を変数に代入するところはこうした。
include_disposed = str_to_bool(request.args.get('include_disposed', default=''))
request.args.get の返す値を str_to_bool で変換している。default=” を指定しているのは、指定しないとクエリに include_disposed がなかった時に None が返ってくるため。
さて、これで期待通りの動作をするようになった。