SQLAlchemyを使ってOracleに接続する

[DockerでDjango環境を作成](https://qiita.com/Masa_Kado/items/96d7e13ebc8150994efe) の続きです。
SQLAlchemyを使ってOracleに接続するまでに行った手順を記載しています。

# (1) 準備：テーブル作成
下記のようなデータを持つテーブル（GYUNYU_MASTER）をOracleに作成しました。

|id|name|
|:--|:--|
|1|コーヒー牛乳|
|2|いちご牛乳|
|3|れもん牛乳|

# (2) SQLAlchemyをインストール
`python/work/requirements.txt`に`sqlalchemy`を追記。

```text:python/work/requirements.txt
django==2.0.3
uwsgi==2.0.17.1
sqlalchemy==1.2.6
```

 1.`docker-compose up --build -d`を実行してコンテナの生成＆起動する。
 2.`docker exec -it コンテナ名 bash`を実行してコンテナの中に入る。
 3.`python`を起動して下記を実行し、SQLAlchemyがインストール出来ている事を確認する。

```python
>>> import sqlalchemy
>>> sqlalchemy.__version__
```

 4.Oracleへ接続するためのデータベースエンジンの設定を追記。

```python:gyunyu/__init__.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine("oracle://ID:パスワード@IPアドレス:ポート番号/データベース名" echo=True)
Session = sessionmaker(bind=engine)
```

 5.セッションを管理する関数を追記。

```python:gyunyu/__init__.py
from contextlib import contextmanager

@contextmanager
def session_scope():
    session = Session()
    try:
        yield session
    except:
        raise
    finally:
        if session is not None:
            session.close()
```

 6.`views.py`にDBから値を取得する処理を追記。

```python:coffee/views.py
from django.shortcuts import render
from django.http import HttpResponse
from gyunyu import session_scope

def index(request):
    with session_scope() as session:
        result = session.execute("select * from gyunyu_master")
        rows = result.fetchall()
    return HttpResponse("Coffee Gyunyu !!! {}".format(rows))
```

下記のようなエラーが発生しました。

```
No module named 'cx_Oracle'
```

# (3) cx_Oracleをインストール
 1.`python/work/requirements.txt`に`cx_oracle`を追記。

```text:python/work/requirements.txt
django==2.0.3
uwsgi==2.0.17.1
sqlalchemy==1.2.6
cx_oracle==6.2.1
```

コンテナの再作成をしてアクセスすると、再びエラーが発生しました。


```
(cx_Oracle.DatabaseError) DPI-1047: 64-bit Oracle Client library cannot be loaded: "libclntsh.so: cannot open shared object file: No such file or directory". See https://oracle.github.io/odpi/doc/installation.html#linux for help (Background on this error at: http://sqlalche.me/e/4xp6)
```

# (4) Oracle Clientのインストール
Oracle Clientが必要なようなので、Instant Clientを[こちら](https://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html)からダウンロードしました。
今回は、`instantclient-basic-linux.x64-12.1.0.2.0.zip`をダウンロードしました。

 1. ダウンロードしたファイルを`python/work`フォルダに保存して、`python/Dockerfile`に追記

```text:python/Dockerfile
ENV ORACLE_HOME=/opt/oracle/instantclient_12_1
ENV LD_RUN_PATH=$ORACLE_HOME
ENV LD_LIBRARY_PATH=$ORACLE_HOME
ENV PATH=$PATH:$ORACLE_HOME

RUN apt-get update
RUN apt-get install -y unzip
RUN unzip instantclient-basic-linux.x64-12.1.0.2.0.zip -d /opt/oracle
RUN ln -s $ORACLE_HOME/libclntsh.so.12.1 $ORACLE_HOME/libclntsh.so
```

コンテナの再作成をしてアクセスすると、再びエラーが発生しました。

```
(cx_Oracle.DatabaseError) DPI-1047: 64-bit Oracle Client library cannot be loaded: "libaio.so.1: cannot open shared object file: No such file or directory". See https://oracle.github.io/odpi/doc/installation.html#linux for help (Background on this error at: http://sqlalche.me/e/4xp6)
```

# (5) libaioのインストール
`libaio.so.1`が必要らしいので、インストールする設定を`python/Dockerfile`に追記。

```text:python/Dockerfile
RUN apt-get install -y libaio1
```

アクセス出来るようになりました。

# (6) NLS_LANGを追記
上記までの作業でOracleからデータを持ってくる事はできたのですが、
nameに登録している文字列（全角文字）が化けていたので、下記を`python/Dockerfile`に追記。

```
ENV NLS_LANG=Japanese_Japan.AL32UTF8
```

もう一度アクセスしてみると、文字化けも無く表示できました。

# さいごに
最終的に`python/Dockerfile`は、下記のようになりました。


```
FROM python:3.6
ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

ENV ORACLE_HOME=/opt/oracle/instantclient_12_1
ENV LD_RUN_PATH=$ORACLE_HOME
ENV LD_LIBRARY_PATH=$ORACLE_HOME
ENV PATH=$PATH:$ORACLE_HOME
ENV NLS_LANG=Japanese_Japan.AL32UTF8

RUN mkdir /home/work
RUN mkdir /home/work/django
COPY work/ /home/work
WORKDIR /home/work
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

RUN apt-get update
RUN apt-get install -y unzip
RUN unzip instantclient-basic-linux.x64-12.1.0.2.0.zip -d /opt/oracle
RUN ln -s $ORACLE_HOME/libclntsh.so.12.1 $ORACLE_HOME/libclntsh.so
RUN apt-get install -y libaio1

CMD ["uwsgi", "--ini", "/home/work/uwsgi.ini"]
```