DjangoでWebアプリを作ってみる。【vol. 09 ここまでの課題の解消】

※[この記事](https://qiita.com/TsJazz27Sumin/items/cf5159d53ebc7f294cd8)の続きです。

### 前回までの課題の解消
次は、Ajaxを使って部分View読み込みをしたいと思っていたのですが、前回までの課題の解消を先にやりたいと思います。

とりあえず画面に表示できるところまで行きましたが、３点課題がありました。

 - 課題１. 毎回のように以下の処理を呼び出しているので、デザイン調整や特定箇所のロジック改善をやりだすと不要な処理がある。特にAPI呼び出しとZip解凍は時間がかかる。
   - API呼び出し。
   - Zip解凍
   - 役員情報を読み込む
   - データを加工する。
 - 課題２. データが無駄に取れている。
 - 課題３. テーブルの列幅が整ってない。

#### 課題１の対応：デザイン調整や特定箇所のロジック改善をやりだすと不要な処理がある。

API呼び出しとZip解凍を除いたリロード用の処理を作る。
サーバー側で新しくメソッドを追加します。

```py
def reload(request):

    file = os.getcwd() + "\XBRL\PublicDoc\jpcrp030000-asr-001_E01777-000_2018-03-31_01_2018-06-19.xbrl"
    data = get_information_about_officers_text(file)
    soup = BeautifulSoup(data.text)
    corporate_officer_list = get_corporate_officer_list(soup)  
    
    context = {'corporate_officer_list': corporate_officer_list, 'corporate_officer_list_count': len(corporate_officer_list)}
                               
    return render(request, 'edinet/corporate_officer_list.html', context)
```

URLのマッピングも追加します。

```py
urlpatterns = [
    path('', views.corporate_officer_list, name='corporate_officer_list'),
    path('call-edinet-api', views.call_edinet_api, name='call_edinet_api'),
    path('reload', views.reload, name='reload'), # 追加
]
```
画面にボタンを追加します。

```html
<a href="{% url 'reload' %}" class="btn btn-lg btn-secondary">Reload</a>
```

これで時間のかかっていたAPI呼び出しとZip解凍をスルーして進めることが出来ました。
今回は、ネタ元のXBRLファイルがローカルにあるので取れる対応です。
ただ、今回のように中間ファイル的なものが存在していなくても、似たようなことはやれます。

例えば、フロント寄りの処理だけ調整したい場合は、JavaやC#の開発でやっていたのは、DIで差し込むService入れ替えてしまうというやり方です。

#### 課題２の対応：データが無駄に取れている。

画面に出して気が付きましたが、データが無駄に取れていました。
明らかにデータが多い。

```py
def get_corporate_officer_list(soup):

    corporate_officer_list = list()

    for table in soup.find_all("table"):
        for tr in table.find_all_next("tr"): # ここに問題あり！！！

            column_value_list = list()

            if len(tr.find_all("td")) > 7:
                for child in tr.children:
                    if child.name == "td":
                        column_value_list.append(child.get_text().replace('\n', '/'))

            if len(column_value_list) > 0:
                corporate_officer = create_corporate_officer(column_value_list)
                corporate_officer_list.append(corporate_officer)

    return corporate_officer_list
```

`table`の要素をfind_allする部分は良かったのですが、次の`tr`を`find_all_next`するところに問題がありました。

 - `table`の要素をループする。
 - ループのたびに`tr`を`find_all_next`する。
 - 結果、`table`の要素数分、HTMLに含まれる`tr`がすべて取れる。

という形です。

これでは、まずいので処理を見直しました。

```py
def get_corporate_officer_list(soup):

    corporate_officer_list = list()

    for table in soup.find_all("table"):
        for tableChild in table.children:

            if tableChild.name == "tbody":
                for tr in tableChild.children:

                    if tr.name == "tr":
                        column_value_list = list()

                        if len(tr.find_all("td")) > 7:
                            for trChild in tr.children:
                                if trChild.name == "td":
                                    column_value_list.append(trChild.get_text().replace('\n', '/'))

                        if len(column_value_list) > 0:
                            corporate_officer = create_corporate_officer(column_value_list)
                            corporate_officer_list.append(corporate_officer)
    
    return corporate_officer_list
```

ちゃんとHTMLの構成に沿って、Childを辿っていくやり方です。
これで`list_count = 17`になり、有価証券報告書に記載の`男性15名　女性２名`とも合致します。

#### 課題３の対応：テーブルの列幅が整ってない。
CSSを適用することにしました。一部、bootstrapの[table-responsive](https://getbootstrap.com/docs/4.1/content/tables/#responsive-tables)を使用しています。
CSSの配置場所や読み込み方は、[こちら](https://qiita.com/egplnt/items/fbd99a08347246f955e2)の記事を参考にさせていただきました。

```css
.edinet-table {
    width:1900px;
}

.position {
    width:200px;
}

.job {
    width:200px;
}

.name {
    width:200px;
}

.birthday {
    width:200px;
}

.biography {
    width:700px;
}

.term {
    width:200px;
}

.stock {
    width:200px;
}
```
HTML側に適用します。

```html
      <div class="table-responsive">
        <table class="table table-bordered table-striped table-dark edinet-table">
          <thead>
            <tr>
              <th class="position">役名</th>
              <th class="job">職名</th>
              <th class="name">氏名</th>
              <th class="birthday">生年月日</th>
              <th class="biography">経歴</th>
              <th class="term">任期</th>
              <th class="stock">株式数</th>
            </tr>
          </thead>
          <tbody>
              {% for corporate_officer in corporate_officer_list %}
              <tr>
                <td class="position">{{corporate_officer.position}}</td>
                <td class="job">{{corporate_officer.job}}</td>
                <td class="name">{{corporate_officer.name}}</td>
                <td class="birthday">{{corporate_officer.birthday}}</td>
                <td class="biography">{{corporate_officer.biography | linebreaks }}</td>
                <td class="term">{{corporate_officer.term}}</td>
                <td class="stock">{{corporate_officer.stock}}</td>
              </tr>
              {% endfor %}
          </tbody>
        </table>
      </div>
```

これでだいぶ体裁が整いました。自分のPCの画面幅は超えているのでtableの縦横スクロールが出ている状態です。`height`は指定していませんが、これは経歴がパラパラと縦に続くためです。

実は、経歴の加工ロジックにも課題があるのですが、こちらは実際の業務で手動も含めて、だいぶ苦労して対応しているので、ロジック修正は控えます。

次は、Ajaxを使って部分View読み込みをしたいと思います。