🔰HTMLかHTLMかすら知らなかった初心者による、Webアプリとの格闘の痕跡(前半)🔰

#この記事の対象者と活用方法
###1.　初心者の方
この記事は、「Python初心者」「Webアプリ初心者」「Flask初心者」「HTML初心者」のいずれかに当てはまる方、その中でも、過去に”初心者向け”で挫折したことのある方を主な対象としています。
私が悩んだ箇所とその原因を全て書いているので、不必要な所は適宜飛ばしつつ、「こいつマジで何も知らないんだな」と鼻で笑いながら読んでください。
ちなみに、特に苦しんだのはパス関係です。
###2. 　初心者に指導している方
初心者がどこでつまずくのか、初心者はどういう勘違いをするのか等、初心者の生態について理解して頂けたら幸いです。
###3. 　初心者に擬態している熟練者の方
本当の初心者というのがどういうものか、この記事を通して今一度思い出して頂けたら幸いです。


#今回作成するWebアプリ
作成するWebアプリの機能は大きく分けて以下の４つです。

>- **ファイルのアップロード**
- **Pythonを用いたファイルの処理**
- **処理後のデータと画像の表示**
- **処理後のデータのダウンロード**

具体的には、15ずつ余ってしまったTシャツとトートバッグを希望者に捌く抽選アプリになります。
参加者の名前と希望の商品を入力すると、抽選結果が表示、保存される仕組みです。
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1010390/82455bba-c601-0631-f33e-f020aab68940.png)


本記事は以下の順番で進めていきます。不必要な箇所は適宜飛ばしてお読みください。

>0.　**開発前の下準備「環境構築」**　仮想環境はなくてもなんとかなる。
1.　**Webアプリの基本的な構造**　　URLに頼りまくる。
2.　**とりあえず画像表示してみる**　もしかして、パスってとんでもなく面倒くさい？ 
3.　**ファイルのアップロード**　　　もしかして、パスってとんでもなく面倒くさい？
4.　**@の外側に変数を連れていく**　sessionは神だが万能ではなかった、、、
5.　**Pythonでの抽選処理**　　　　　例外パターンが意外と多い
6.　**関数を外から連れてくる**　　　もしかして、パスってとんでもなく面倒くさい？
7.　**結果の出力**　　　　　　　　　csvかexcelか。Pandasのおかげで意外と簡単
8.　**見た目を整える**　　　　　　　ここで登場HTLM


#0.　**開発前の下準備「環境構築」**
なんとなくアナコンダをインストールしたはいいものの、仮想環境というのはなんだか怖そうだったので、恥ずかしながら調べもせず放置していました。
仮想環境というのは「使い捨ての環境」のようなもので、最大の魅力はその気軽さにあります。
ひとたび仮想環境を作れば、他のことは何も気にせず好きなライブラリの好きなバージョンを入れまくることが可能です。こうすることで、Cの開発のために〇〇のライブラリのバージョンを新しくしたら、既存のBとDの方で不具合が、、、というような煩わしいイベントから解放されます。また、何か問題が起こった時にはその仮想環境ごと切り捨てて新しく作りなおす、ということも可能です。アナコンダさえインストールされていれば作り方はとても簡単なので、ざっと見ていきましょう。詳しいことは以下の参考記事に載っています。

参考にした記事→ [【初心者向け】Anacondaで仮想環境を作ってみる](https://qiita.com/ozaki_physics/items/985188feb92570e5b82d)

###仮想環境を作る
ターミナルを開き、環境名を決め、以下のように入力します。
＄を入れてエラーが出たら、＄を消してみて下さい。

```terminal:
$ conda create -n 環境名
```

多くの場合、同時にpythonもインストールしたいので以下のようにします。

```terminal:
$ conda create -n 環境名 python
または
$ conda create -n 環境名 python=バージョン
```

また、この段階でアナコンダにあるすべてのライブラリをインストールしたい場合は、

```terminal:
$ conda create -n 環境名 anaconda
```

これで仮想環境は完成です。

###仮想環境を使う・ライブラリを突っ込みまくる

まず、作った環境を起動します。

```terminal:
MacOSのとき
$ source activate 環境名

Windowsのとき
C:(省略)>activate 環境名
```

これで実行環境が指定した仮想環境に変わりました！

次は使用するライブラリのインストールですが、これもマジで簡単です。
基本的には、以下の一行で済みます。個人的には、謎のpipと距離を置けそうなことにかなり感動しました。

```terminal:
$ conda install ライブラリ名
```


我々初心者が使う基本的なライブラリならこれで問題ないのですが、もしもcondaが対応していない場合は先ほどの参考記事の方で対処法に触れてくださっているので、そちらに飛んでみて下さい。





#1.　**Webアプリの基本的な構造**
今回はFlaskを用いてWebアプリを作成します。
「URLに飛んだらHello worldが出てくるWebアプリ」を作り、基本的な構造を説明していきます。
ひとまずは、htmlとpythonのファイルをURLで関連付けている、ということが分かればOKです。

参考にしまくった記事→ [Webアプリ初心者のFlaskチュートリアル](https://qiita.com/kiyokiyo_kzsby/items/0184973e9de0ea9011ed)

##ディレクトリ構成

```
(any directory)
 ├app/
 │　├templates/
 │　├static/
 │　└app.py
 └run.py
```

基本的にはこんな感じです。（/はフォルダを意味しています。）
実行するのはrun.pyで、アプリを実際に動かす仕組み部分はapp.py
templatesフォルダはhtmlファイルを入れる箱、staticフォルダは画像等のファイルを入れる箱です。
なお、any directory以外のフォルダ名は決まっているので、一字一句変えてはなりません。私はtemplatesを単数形にしていたせいで１時間謎のエラーに苦しみました。

##1-1.  HTMLも使わずHello Worldまで！
まずはHTMLも使わず、ただHello Worldという文字列を表示します。
説明は後ろにまとめてあるので、ひとまずapp.pyとrun.pyを書いてみましょう。

```python:app.py
from flask import Flask

#Flaskオブジェクトの生成　
app = Flask(__name__)

#アクセスしたURLが「/」だった場合、"Hello World"の文字列を返す
@app.route("/")
def hello():
    return "Hello World"

#おまじない
if __name__ == "__main__":
    app.run(debug=True)
```

```python:run.py
from app.app import app

if __name__ == "__main__":
    app.run()
```

これらのコードが書けたら、あとはWebサーバを立てるだけです。
コマンドを開き、以下のように実行してみて下さい。

```terminal:
python run.py
```
うまく行かない方はディレクトリの移動が出来ていない可能性があります。
<details><summary>ディレクトリの移動方法が分からない方はこちら</summary>
コマンドプロンプトなどで何かのファイルを実行するには、そのファイルが保存されているディレクトリに移動しなくてはいけません。
今回でいうと、run.pyを実行するためには先ほど(any directory)と記載していた箇所(私の場合testという名前のフォルダ)に移動する必要があるわけです。
移動のためには行きたい場所の住所が必要なので、エクスプローラーで目的のディレクトリの絶対パスを取得します。
絶対パスが分かったら、コマンドで「cd 調べた絶対パス」を実行してみて下さい。
（cdはチェンジディレクトリを表しています。）</details>
上手く実行できると以下のような文章が出てきます。

```
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
```

ここで貰えるURL　http://127.0.0.1:5000/ をブラウザで開いてみて下さい。
Hello Worldがあなたを待っているはずです。
この一連の作業をするとコマンドの入力ができなくなるので、使用を再開したい場合はCTRL+Cを押してください。

それではapp.pyの中身の説明をしていきます。
####@app.route("/")について
厳密なことは分からないので雑にいきます。
@app.route("/")　が実行されるのは、http://127.0.0.1:5000/ にアクセスしたときで、
@app.route("/abc") が実行されるのは、http://127.0.0.1:5000/abc にアクセスしたときです。

####@app.route()内の関数について
関数の定義はしていますが、一見すると実行できていないように見えます。
始めは、何故動くのだ！！と疑問に思ったのですが、実は@app.route()、書かれた関数をそのまま実行する仕組みになっているらしいです。
じゃあ関数にする必要があるのか？という話になりますが、正直よくわかりません。
ただ、別の@で使った関数名を用いるとエラーが出たので、そこは注意した方がよさそうです。
なお、１つの@の中で関数を二つ定義してみたところ、１つ目しか実行されませんでした。

今回はreturn の戻り値が文字列だったため、ただ文字が表示されただけでした。
次以降では、表示用のHTMLファイルを用いていきます。

##1-2. HTMLを使って綺麗なHello World！

templateフォルダにhome.htmlというHTMLファイルを作成します。

```html:home.html
<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
    <h1>Hello World</h1>
    </body>
</html>
```
続いて、app.pyの方を以下のように変えます。
render_templateという、htmlを表示する関数を使いましょう。

```python:app.py
from flask import Flask,render_template

app = Flask(__name__)

@app.route("/")
def hello():
    return render_template("home.html")

if __name__ == "__main__":
    app.run(debug=True)
```

先ほどと同様に実行すると、良い感じの Hello World が出てくるはずです。

##1-3. Hello WorldからのHello World！

続いて画面送りを実装し、home.htmlから別の画面に飛べるようにしてみましょう。
templatesの中に、second.htmlというファイルを追加します。
（中身は本当に何でも構いません。home.htmlと区別できれば大丈夫です。）
今回は一部＜ｂ＞タグで太字にしてみました。

```html:second.html
<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
    <h1>Hello World <b>season2</b></h1>
    </body>
</html>
```

続いて、home.htmlに、form情報を追加します。

```html:home.html
<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
    <h1>Hello World</h1>
    <form action="/second"><input type="submit" value="次へ"></form>
    </body>
</html>
```

formのaction=""の中身は、次のURLを表しています。
value=""の中身は、クリックする箇所に表示する文字を表しています。
こうすることで、画面上の指定箇所をクリック→次のURLに飛ぶという流れが出来ました。
それでは、次のURLと先ほど作成したhtmlファイルを紐づけましょう。
app.pyに、新たなURLへの対応を追加します。

```python:app.py
from flask import Flask,render_template

app = Flask(__name__)

@app.route("/")
def home():
    return render_template("home.html")

@app.route("/second")
def second():
    return render_template("second.html")

if __name__ == "__main__":
    app.run(debug=True)
```

これで、無事　**Hello World season2**　が表示できたことと思います。
楽しいですね、うまく行けば。


#2.　**とりあえず画像表示してみる**
ここは簡単なので、身構えなくても大丈夫です。
今回は霞を食べている仙人の画像（KASUMI.png）を表示したいと思います。

staticフォルダ内にimagesフォルダを作成し、そこに表示したい画像を保存します。
（拡張子はpngでもjpegでも大丈夫です。）
今回はhome.htmlを以下のように改変し、app.pyはそのままで画像を表示していきます。

```html:home.html
<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
    <img src="\static\images\KASUMI.png" alt="霞を食べている仙人" width="300" height="300">
    </body>
</html>
```

alt=""の内容は代替テキストと呼ばれるもので、視覚障害のある方等のための音声読み上げなどに使います。なくても大丈夫ですが、書いておきましょう。
widthとheightは画像のサイズを指定するものです。何も書かなければもともとのサイズで表示されます。縦横比の歪みが気になる場合は縦と横のどちらかのみを指定することで、比率を損なうことなくサイズ調整が出来ます。


###パスとの闘い①
src=""の中には、パスを書きます。絶対パスであれば楽なのですが、後述の理由から相対パスを使います。
まず前提として、相対パスにはスタート地点(視点)があります。
結論から言うと、相対パスは実行する場所をスタート地点として書くようです。home.html目線ではないのです。
今回はrun.pyを基準として画像の格納されているディレクトリのパスを書くので、
"\static\images\KASUMI.png"が正しいパスとなります。（最初の\忘れがち）
うまく行かない人は、「\」を「/」に変えてみて下さい。

ここでは画像の名称をアルファベット表記にしましたが、日本語でも一応いけます。
しかしいつでもそれが通用するとは限らないので、名称はおとなしくアルファベット表記にしておきましょう。

この辺りで、「画像表示に相対パスが必要なら、htmlファイル表示の時にはなぜいらなかったの？」
という疑問を抱いた方がいらっしゃるかもしれないので説明しておきます。
flaskはめちゃめちゃに有能なので、render_template()という神関数を使ったときは自動的に、templatesフォルダからhtmlファイルを探してきてくれるのです。超絶便利な反面、templatesなどのフォルダ名を規定通りに設定しないと即エラーなので気を付けましょう。
render_template()が相対パスを勝手に見繕ってくれるということは、誰がどんな環境で実行しても同じ結果を返せるということです。絶対パスを使っていては、このような汎用性を生み出すことは出来ません。
ディレクトリの位置を変えたり、一度書いたコードを他のPCで実行したりと、絶対パスはすぐに変わってしまうものです。その都度コードを書き換えるのは非常にだるいので、今後も張り切って相対パスに挑んでいきましょう。

なお、お気づきの方もいらっしゃると思いますが、これは私とパスとの長い闘いの序章、幕開けにすぎません。

気を取り直してrun.pyを実行しましょう。

![2021-09-27 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1010390/ae098d98-ea16-5d68-7de2-bc1a74056a6f.png)

このような表示が出ればOKです。

#3.　ファイルのアップロード
ここは個人的に苦しんだポイントですが、その苦しみを繰り返さないことこそが本記事の目標です。
大船に乗ったつもりで読んでいってください。

この章では、home.htmlでファイルをアップロードしてもらい、ファイルの保存完了を確認するところまでを扱っていきます。

始めに、ファイルの保存先として、staticの中にuploadedというフォルダを作っておきます。
ここの名称はなんでも構いません。
フォルダの準備ができたら、保存完了を知らせるページから作っていきましょう。
templatesの中に、uploaded.htmlというファイルを作成します。

```html:uploaded.html
<!DOCTYPE html>
<html>
    <body>
        保存が完了しました（各自、自分への誉め言葉を書いておくといいです。）
    </body>
</html>
```

続いて、home.html内にファイルアップロードのフォームと送信ボタンを作ります。
1行目のaction=""の中には次のURLを、
2行目のname=""の中には仮の名前を、
2行目のaccept=""の中には受け入れる拡張子を書きます。
（それ以外の要素が何しているのかは正直よくわかっていません。）

```html:home.html
<!DOCTYPE html>
<html>
    <body>
        <form action="/upload" method="POST" enctype="multipart/form-data">
        <input type="file" name="uploaded_file" accept=".xls,.xlsx">
        <input type="submit" value="送信">
        </form>
    </body>
</html>
```
次に、app.py側を改変していきます。ここは少し大変ですが、後ろに解説を載せてあるので安心してください。

```python:app.py
from flask import Flask,render_template,request

app = Flask(__name__)

@app.route("/")
def home():
    return render_template("home.html")

@app.route("/upload", methods=["POST"])
def upload():
    f = request.files["uploaded_file"] 
    filepath = 'app/static/' + f.filename
    f.save(filepath)
    return render_template("uploaded.html")

if __name__ == "__main__":
    app.run(debug=True)
```



新たにインポートしたrequestというのがめちゃめちゃ働きます。
requestを使って、取ってきたファイルの扱いをfに一任し、
f.filenameでファイル名の取得
f.save()でファイルの保存をやっている、という感じですね。
これがインスタンス化に値するのかどうかは、自信がないので言及しないことにします。

###パスとの闘い②
また来てしまいました。ここでは私を苦しめたfilepathについて述べていきます。
flaskのrequestやsaveについて調べると、save()に渡すのは「保存先のパス」と書かれていることが多いです。
しかしながら、"app/static/"　だけではどう頑張ってもエラーになってしまいます。
その原因は、求められているパスが「保存先のフォルダのパス」ではなく、「保存された後の、ファイル自身の相対パス」であることでした。
そのためfilepathには、f.filennameで取得したファイル名（拡張子付き）まで入れなくてはならないのです。
/の有無やスペルミスなどにも十分注意したうえでお使いください。

ここまで準備が出来たら、run.pyの実行です。
URLを開いたら、言われるがまま適当なファイルをアップロードしましょう。
うまく行けば祝福のメッセージとともに、uploadedへファイルが保存されるはずです。


区切りがいいので前半はここまでにします。後半はこちら→[HTMLかHTLMかすら知らなかった初心者による、Webアプリとの格闘の痕跡(後半)](https://qiita.com/Marukutekenohaetabuttai/items/48421bdb66c138125e00)