DjangoにてPythonでajaxしてみる

この記事は [Django Advent Calendar 2018](https://qiita.com/advent-calendar/2018/django) 12/7の記事です。


# この記事は
**Django**にて**Python**で**ajax**する方法を書きとめます。

パッと検索すると、PHPでajaxするサンプルはヒットするものの、
Pythonのサンプルがみつからなかった(記事の初稿当時)ので、
調べて、試して、悩んで、書いてみました。

**調べながら、整理しながら、書きのこす**というスタンスの記事です。


# 著者のレベル
おしごとで最近使うメイン武器は VC#,LabVIEW,組み込み系C++ など。
コード書く時間が長いけれど、本当の興味は **工学的なしくみ各種**

WEBプログラミングは、初心者です。
大量のインプット沼と、高速なトレンド変化の香りがしていたので、
ずっと避けまくってちかづかなかった。。

記事に出てくるスキルを表にすると、下記のよう。

|プログラミング言語|レベル|
---|---
|初めてのWEBプログラミング|HTMLでmarqueeしたことあります！！！|
|Python|Qiitaの面白記事を読む専門|
|Django|"ドジャンゴ"って読みにくくない？と思ってた|
|JavaScript|なんでそこにも`function(){`って書くんだ・・？って今でも思ってる|
|BootStrap|WEBデザイン界の"味覇"と聞いてる|

# 想定読者
- Djangoのチュートリアルを終えている
- Pythonをかじったことがある
- わからないことは調べてしまえスタンスな人
- 通りすがりの強レビュワー

# github
忘れないうちにコード置いておきますね。
こちら >> [ajax_Django](https://github.com/kakimochi/ajax_Django)

Anacondaインストールして、Django(2.0以上)をインストールすればローカルで実行できます。
`python manage.py runserver` コマンドを知らなければ、Djangoのチュートリアルへ行きましょう。

# サンプルの実行
三カ月後の他人は自分ですからね！！書き残しておきます。

- インストール

|手順|ノート|
---|---
|Anacondaをインストールする|すでにPython環境あるひとはそれを使いましょ|
|Python仮想環境をつくる|Anaconda Navigatorでつくれます|
|Djangoをインストールする| `pip install Django` |

- ローカルでサンプルを実行

|手順|ノート|
---|---
|shellをひらく|コマンドプロンプト、PowerShell、Git Bash、などなんでもよい|
|サンプルのmanage.pyがあるフォルダへ移動する||
|`python manage.py runserver`する|エラー出たらDjangoチュートリアルに戻って、問題の切り分け！|
|ブラウザで `localhost:8000` をひらく||

# やりたいこと
Djangoチュートリアルでは、下記のように、views.pyがhtmlをrenderして返してくれます。
ただform送るたびに、全画面が更新されるのは、やっぱつれぇわ。。って思いました。

```python

def test_ajax_app(request):
    hoge = "Hello Django!!"

    return render(request, "test_ajax_app/test_ajax_app.html", {
        "hoge": hoge,
    })
```

なので、form送ったら、更新された部分HTMLだけを返してほしい。
つまりは、**ajax**したいわけですね。

# さてコード解説しますね
みるべきコードは、主に下記です。

|file|役割|
---|---
|test_ajax_app/views.py|formを送信されたら、なにか処理して、HTMLをレンダリングして返します|
|test_ajax_app/urls.py|formからURLとして呼ばれると、処理を渡すpyファイルまでルーティングします|


# test_ajax_app/test_ajax_app.html
- 使用するJavaScriptライブラリ
    - jQuery (slimじゃないやつ)が必要です。また、この記事の趣旨としてはBootStrapは不要です。


```HTML
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <!-- <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script>

```

- formにかかわる部分
    - `action="/test_ajax_app/ajax/"`としている。これは`urls.py`によってルーティングされる。
    - `{% csrf_token %}`Djangoでは、これがformタグ内にないと動きません。

```HTML
    <form name="name_form" action="/test_ajax_app/ajax/" method="POST">
    <!-- <form name="name_form" action="/test_ajax_app/" method="POST"> -->
      {% csrf_token %}
      <input type="text" id="id_input_text" name="name_input_text" value="999.99">
      <input class="btn" type="submit"> 
    </form>

    <span id="id_div_ajax_response">
      AJAX RESPONSE
    </span>
```

このsubmitボタンを押したら、Pythonでなんらかの処理をして、
このHTML上に部分的なHTMLとして返したいのです。

# test_ajax_app/urls.py
前述のformにあった`action="/test_ajax_app/ajax/"`をルーティングしている部分です。
`/test_ajax_app/`みたいなアクセスのときは、`views.py`の`test_ajax_app()`を呼びます。
`/test_ajax_app/ajax/`みたいなアクセスのときは、同じく`views.py`の`test_ajax_response()`を呼びます。

```Python
urlpatterns = [
    path("", views.test_ajax_app),
    path("ajax/", views.test_ajax_response),
]
```

# test_ajax_app/test_ajax_app.html (ふたたび)
今度は JavaScript の部分をみていきます。

```JavaScript
      $("form").submit( function(event) {
        event.preventDefault();
        var form = $(this);
        $.ajax({
          url: form.prop("action"),
          method: form.prop("method"),
          data: form.serialize(),
          timeout: 10000,
          dataType: "text",
        })
        .done( function(data) {
          alert("done");
          $("#id_div_ajax_response").text(data);
        })
      });
```
- formのsubmitイベントをここで処理する

```JavaScript
      $("form").submit( function(event) {
```


- デフォルトのform操作を一時的に防止する

```JavaScript
        event.preventDefault();
```

- ajaxの設定
    - url: formタグのactionプロパティをセットします。なので、`"/test_ajax_app/ajax/"`が格納される
    - method: methodプロパティ、今回は POST ですね
    - data: `serialize()`は、formタグ内の送信されるデータを `AAA&BBB&CCC`みたいな感じに結合します。
        - もちろん、`form.serialize() + "&" + "DDD"` みたいに文字列結合しても一緒に送信できます。

```JavaScript
        $.ajax({
          url: form.prop("action"),
          method: form.prop("method"),
          data: form.serialize(),
          timeout: 10000,
          dataType: "text",
        })
```

# test_ajax_app/views.py (ふたたび)
serialize()されて、ajaxで送信されてきたデータは、
下記の `request` に格納されてます。

```Python
def test_ajax_response(request):
    input_text = request.POST.getlist("name_input_text")
    hoge = "Ajax Response: " + input_text[0]

    return HttpResponse(hoge)
```

`name_input_text`というnameを持ったinputタグに入力されたデータは、
下記で取り出すことができます。

```Python
    input_text = request.POST.getlist("name_input_text")
```

そして、部分的なHTMLとして返すには、HttpResponse()を使います。

```Python
    return HttpResponse(hoge)
```

# test_ajax_app/test_ajax_app.html (さんどめ)
Pythonから返ってきた部分的なHTMLは、
どこに到着するのでしょうか。

```JavaScript
      $("form").submit( function(event) {
        event.preventDefault();
        var form = $(this);
        $.ajax({
          url: form.prop("action"),
          method: form.prop("method"),
          data: form.serialize(),
          timeout: 10000,
          dataType: "text",
        })
        .done( function(data) {
          alert("done");
          $("#id_div_ajax_response").text(data);
        })
      });
```

そうですね。それっぽい箇所ありますね。
下記の `data` に格納されて返ってきます。

```JavaScript
        .done( function(data) {
          alert("done");
          $("#id_div_ajax_response").text(data);
        })
```

# おわりに
図式があったほうがわかりやすそうなのと、参考文献かかなきゃならないですが、
ここまででタイムアップなので、あとで追記します。

こういうしくみをひも解いて行くのは、あとでしくみを作る時にも役立ちますし、
なんといっても楽しいですね！！