メモ：Python + DjangoでPostgresを使った簡単な画面


Windows + Python3.9でやる。Djangoはversion 3.2.8
今回はRestではなく通常の画面を想定するのでDjango REST frameworkは使わない。

## Djangoの準備
Pythonはインストール済とする。

とりあえず仮想環境で動かしてみるのでvirtualenvをインストール
pip install virtualenv
適当なフォルダに環境を作って
virtualenv env1
環境に入る
Script/actibate.bat実行
そのままpipでインストール
pip install django
続いてインポートが正常に出来るか確認してみる
py
import django
print(django.get_version())

## 画面の作成
とりあえず作成した仮想環境内でテストプロジェクトを作成
django-admin startproject tespt
この中に入って起動する。

```
(env1) C:\python\env1\testpj>py manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the
Run 'python manage.py migrate' to apply them.
October 19, 2021 - 16:06:43
Django version 3.2.8, using settings 'testpj.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
```

でアップデート警告があるがきにせずlocalhost:8000に繋ぐと
The install worked successfully! Congratulations!
と表示されて起動している事がわかる。

myappというプロジェクトを作成する
python manage.py startapp myapp

testpjのsettings.pyのINSTALLED_APPSに追加

```
INSTALLED_APPS = [
	'myapp',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```
同じ場所のurls.pyにはmyappのurlsを見るようにincludeを追加
※fromにincludeを追加する必要はある

```
urlpatterns = [
    path('admin/', admin.site.urls),
    path('myapp/', include('myapp.urls')),
]
```

適当に表示する画面を作成するため、
とりあえずteplatesフォルダを作成してindex.htmlを配置。(UTF-8で記述する)
プロジェクトのルートの下にtamplatesを作成したので別途setting.pyも触る。

```
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>テスト</title>
</head>
<body>
    <h1>テスト</h1>
    <div>{{message}}</div>
</body>
</html>
```

```py:setting.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [ BASE_DIR/'templates'], ★ここを変更
        'APP_DIRS': True,
```

view.pyではこのテンプレートにアクセスするための設定と、渡す値を書いてやる

```
from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
def index(request):
  params = {
    'message':'メッセージ'
  }
  return render(request, 'myapp/index.html', params)
```

http://localhost:8000/myapp/
で繋がった。


## Postgresへの接続
psycopg2とpsycopg2-binaryが必要になるのでpipでインストール
settings.pyのデータベース設定を変更

ORマッピングが面倒。[マニュアル](https://docs.djangoproject.com/ja/3.2/topics/db/models/)
一応素のSQLも[実行](https://docs.djangoproject.com/en/3.2/topics/db/sql/)できるらしい。
とりあえずマイグレーションが必要らしいので一度実行

python manage.py makemigrations
python manage.py migrate

スーパーユーザを作成してみる
python manage.py createsuperuser

ここまでは簡単。
この先既存テーブルからデータを引っ張ってこようと思うと、単純にSQL発行だけしたいがクラスを作る必要がある。
[この記事](https://qiita.com/bow_arrow/items/2641267e1e088ef58d18)とか、[公式](https://docs.djangoproject.com/en/3.2/howto/legacy-databases/)
を参考に
python manage.py inspectdb
を実行して、[この前](https://qiita.com/DiveMasakazu/items/a2ff615bcea1f8fbd433)Node.jsでテストしたテーブルのクラスを作成

```python:models.py
class Test(models.Model):
    id = models.IntegerField(blank=True, null=True)
    name = models.CharField(max_length=50, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'test'


class Usr(models.Model):
    usr = models.CharField(max_length=100, blank=True, null=True)
    pass_field = models.CharField(db_column='pass', max_length=100, blank=True, null=True)  # Field renamed because it was a Python reserved word.

    class Meta:
        managed = False
        db_table = 'usr'
```

ここで、Djangoの制限としてidカラムはプライマリーキーの制限云々がある模様。
DB側で設定していないが、とりあえず上のオプションのところをnull=False,primary_key=Trueに変更。
view.pyにQuery文を書いて無理やり出力。

```python:view.py
def index(request):
  if "id" in request.GET:
    sql = "SELECT id,name FROM test WHERE id = %(id)s"
    params = {"id": request.GET.get("id")}
    names = Test.objects.raw(sql, params)
  
  v_params = {
    'message': list(names)[0].name if len(list(names)) == 1 else 'NG'
  }
  return render(request, 'myapp/index.html', v_params)
```

[ここ](https://intellectual-curiosity.tokyo/2018/12/12/django%E3%81%A7%E7%B4%A0%E3%81%AEsql%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/#i-6)
を見ると、モデル定義しなくてもSQL発行出来たりはするらしい。

Pythonにはあまり馴染みがないのもあるが、そんなに好みの感じではなかった。