Week 9 Flask

ようこそ!

  • これまでの数週間で、皆さんは数多くのプログラミング言語、テクニック、そして戦略を学んできました。

  • 実際、このクラスは「Cのクラス」や「Pythonのクラス」というよりも、むしろ「プログラミングのクラス」としての側面が強く、将来のトレンドを追い続けることができるようになっています。

  • この数週間で、皆さんはプログラミングについて「どのように学ぶか」を学んできました。

  • 今日は、HTMLとCSSから一歩進んで、HTML、CSS、SQL、Python、そしてJavaScriptを組み合わせ、独自のWebアプリケーションを作成する方法を学びます。

  • 今週学ぶスキルを使って、最終プロジェクトを作成することを検討してもよいでしょう。

http-server

  • これまでのところ、皆さんが見てきたHTMLはすべて事前に作成された静的なものでした。

  • かつて、皆さんがページにアクセスしたとき、ブラウザはHTMLページをダウンロードし、皆さんはそれを閲覧することができました。これらは「静的」ページと呼ばれます。HTMLにプログラムされた内容が、ユーザーがWebブラウザでダウンロードして表示するもの(クライアント側)と全く同じだからです。

  • 「動的」ページとは、Pythonや同様の言語を使用してHTMLをその場で生成する機能を指します。したがって、ユーザーの入力や動作に基づいてコードによってサーバー側で生成されるWebページを持つことができます。

  • これまではWebページを提供するために http-server を使用してきました。今日は、Webアドレスを解析し、提供されたURLに基づいてアクションを実行できる新しいサーバーを利用します。

さらに、先週は以下のようなURLを見ました。

https://www.example.com/folder/file.html

file.html は、example.comfolder というフォルダ内にあるHTMLファイルであることに注意してください。

Flask

  • 今週は、https://www.example.com/route?key=value のような「ルート」を扱えるようになります。ここでは、URLで指定されたキーと値を使用して、サーバー側で特定の機能を生成できます。

Flask は、PythonでFlaskフレームワーク(またはマイクロフレームワーク)を使用してWebアプリケーションをホストできるようにするサードパーティのライブラリです。

  • cs50.dev のターミナルウィンドウで flask run を実行することで、Flaskを実行できます。

  • そのためには、app.py というファイルと requirements.txt というファイルが必要です。app.py には、FlaskにWebアプリケーションの実行方法を指示するコードが含まれています。requirements.txt には、Flaskアプリケーションの実行に必要なライブラリのリストが含まれています。

以下は requirements.txt の例です。

Flask

このファイルには Flask だけが含まれていることに注意してください。これは、Flaskアプリケーションを実行するためにFlaskが必要だからです。

以下は、app.py 内の非常にシンプルなFlaskアプリケーションです。

# Says hello to world by returning a string of text

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
    return "hello, world"

/ ルートが単に hello, world というテキストを返していることに注意してください。

HTMLを実装するコードを作成することもできます。

# Says hello to world by returning a string of HTML

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
    return '"en">hellohello, world'

単なるテキストを返すのではなく、HTMLを提供していることに注意してください。

アプリケーションを改良して、テンプレートに基づいたHTMLを提供することもできます。templates というフォルダを作成し、そのフォルダ内に以下のコードを含む index.html というファイルを作成します。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        hello

        hello, {{ name }}

二重の {{ name }} は、後でFlaskサーバーから提供されるもののプレースホルダーであることに注意してください。

次に、templates フォルダがあるのと同じフォルダに app.py というファイルを作成し、次のコードを追加します。

# Uses request.args.get

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
    name = request.args.get("name", "world")
    return render_template("index.html", name=name)

このコードでは、app をFlaskアプリケーションとして定義しています。次に、app/ ルートが、引数 name を伴って index.html の内容を返すように定義しています。デフォルトでは、request.args.get 関数はユーザーから提供された name を探します。名前が提供されない場合、デフォルトで world になります。@app.route はデコレータとして知られています。

ターミナルウィンドウで flask run と入力すると、このWebアプリケーションを実行できます。Flaskが動作しない場合は、上記の各ファイルの構文が正しいことを確認してください。さらに、Flaskが動作しない場合は、ファイルが以下のように整理されていることを確認してください。

/templates
    index.html
app.py
requirements.txt
  • 実行すると、リンクをクリックするように促されます。そのWebページに移動したら、ブラウザのURLバーにあるベースURLに ?name=[あなたの名前] を追加してみてください。

フォーム

プログラムを改良していきます。ほとんどのユーザーはアドレスバーに引数を入力しないことを私たちは知っています。代わりに、プログラマはユーザーがWebページのフォームに入力することに頼ります。したがって、index.html を次のように修正できます。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        hello

         action="/greet" method="get">
             autocomplete="off" autofocus name="name" placeholder="Name" type="text">
             type="submit">Greet

ユーザーの名前を受け取り、それを /greet というルートに渡すフォームが作成されたことに注意してください。autocomplete はオフになっています。さらに、name というテキストの placeholder が含まれています。また、Webページをモバイル対応にするために meta タグがどのように使用されているかにも注目してください。

さらに、app.py を次のように変更できます。

# Adds a form, second route

from flask import Flask, render_template, request

app = Flask(__name__)

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

@app.route("/greet")
def greet():
    return render_template("greet.html", name=request.args.get("name", "world"))

デフォルトのパスはユーザーが名前を入力するためのフォームを表示することに注意してください。/greet ルートは、そのWebページに name を渡します。

この実装を完成させるには、templates フォルダに greet.html という別のテンプレートが次のように必要になります。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        hello

        hello, {{ name }}

このルートがユーザーへの挨拶とそれに続く名前をレンダリングするようになることに注目してください。

テンプレート

  • index.htmlgreet.html の両方のWebページには、同じデータの多くが含まれています。ボディ(本文)部分はユニークにしつつ、同じレイアウトをページ間でコピーできれば便利だと思いませんか?

まず、layout.html という新しいテンプレートを作成し、次のようなコードを書きます。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        hello

        {% block body %}{% endblock %}

{% block body %}{% endblock %} により、他のHTMLファイルから他のコードを挿入できることに注意してください。

次に、index.html を次のように修正します。

{% extends "layout.html" %}

{% block body %}

     action="/greet" method="get">
         autocomplete="off" autofocus name="name" placeholder="Name" type="text">
         type="submit">Greet

{% endblock %}

{% extends "layout.html" %} という行が、このページのレイアウトをどこから取得するかをサーバーに伝えていることに注意してください。次に、{% block body %}{% endblock %} が、layout.html に挿入されるコードを指示しています。

最後に、greet.html を次のように変更します。

{% extends "layout.html" %}

{% block body %}
    hello, {{ name }}
{% endblock %}

このコードがいかに短く、コンパクトになっているかに注目してください。

リクエストメソッド

  • ユーザー名やパスワードがURLに表示されてしまうため、get を利用するのが安全ではないシナリオが想像できるでしょう。

この問題を解決するために、post メソッドを利用できます。app.py を次のように修正します。

# Switches to POST

from flask import Flask, render_template, request

app = Flask(__name__)

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

@app.route("/greet", methods=["POST"])
def greet():
    return render_template("greet.html", name=request.form.get("name", "world"))

/greet ルートに POST が追加されたこと、そして request.args.get ではなく request.form.get を使用していることに注目してください。

  • これは、サーバーに対して仮想的な封筒の「中身」を見るように指示し、URLの中に post の項目を露出させないようにします。

さらに、getpost の両方に単一のルートを利用することで、このコードをさらに進化させることができます。これを行うには、app.py を次のように修正します。

# Uses a single route

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        return render_template("greet.html", name=request.form.get("name", "world"))
    return render_template("index.html")

単一のルーティングで getpost の両方が行われていることに注目してください。ただし、ユーザーが要求したルーティングのタイプに基づいて適切にルートを分けるために、request.method が利用されています。

これに合わせて、index.html を次のように修正できます。

{% extends "layout.html" %}

{% block body %}

     action="/" method="post">
         autocomplete="off" autofocus name="name" placeholder="Name" type="text">
         type="submit">Greet

{% endblock %}

フォームの action が変更されたことに注意してください。

しかし、このコードにはまだバグがあります。新しい実装では、誰かがフォームに名前を入力しなかった場合、名前なしで Hello, と表示されてしまいます。app.py を次のように編集することで、コードを改善できます。

# Moves default value to template

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        return render_template("greet.html", name=request.form.get("name"))
    return render_template("index.html")

name=request.form.get("name")) に変更されたことに注目してください。

最後に、greet.html を次のように変更します。

{% extends "layout.html" %}

{% block body %}

    hello,
    {% if name -%}
        {{ name }}
    {%- else -%}
        world
    {%- endif %}

{% endblock %}

名前が特定されなかった場合にデフォルトの出力を許可するために、hello, {{ name }} がどのように変更されたかに注目してください。

Frosh IMs

  • Frosh IMs(フロッシュ・アイエムズ)は、学生が学内スポーツ(イントラミュラル・スポーツ)に登録できるWebアプリケーションです。

  • hello 関連のウィンドウをすべて閉じ、ターミナルウィンドウで mkdir froshims と入力してフォルダを作成します。次に、cd froshims と入力してこのフォルダに移動します。その中に、mkdir templates と入力して templates というディレクトリを作成します。

次に、froshims フォルダで code requirements.txt と入力し、次のようにコードを書きます。

Flask

以前と同様に、Flaskアプリケーションを実行するにはFlaskが必要です。

最後に、code app.py と入力し、次のようにコードを書きます。

# Implements a registration form using a select menu, validating sport server-side

from flask import Flask, render_template, request

app = Flask(__name__)

SPORTS = [
    "Basketball",
    "Soccer",
    "Ultimate Frisbee"
]

@app.route("/")
def index():
    return render_template("index.html", sports=SPORTS)

@app.route("/register", methods=["POST"])
def register():

    # Validate submission
    if not request.form.get("name") or request.form.get("sport") not in SPORTS:
        return render_template("failure.html")

    # Confirm registration
    return render_template("success.html")

name または sport フィールドが正しく入力されていない場合に、ユーザーに失敗メッセージが表示されるよう、failure オプションが用意されていることに注目してください。

次に、templates フォルダに index.html というファイルを code templates/index.html と入力して作成し、次のようにコードを書きます。

{% extends "layout.html" %}

{% block body %}
    Register
     action="/register" method="post">
         autocomplete="off" autofocus name="name" placeholder="Name" type="text">
         name="sport">
             disabled selected value="">Sport
            {% for sport in sports %}
                 value="{{ sport }}">{{ sport }}
            {% endfor %}

         type="submit">Register

{% endblock %}

次に、code templates/layout.html と入力して layout.html というファイルを作成し、次のようにコードを書きます。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        froshims

        {% block body %}{% endblock %}

4番目に、templates フォルダに次のように success.html というファイルを作成します。

{% extends "layout.html" %}

{% block body %}
    You are registered!
{% endblock %}

最後に、templates フォルダに次のように failure.html というファイルを作成します。

{% extends "layout.html" %}

{% block body %}
    You are not registered!
{% endblock %}
  • flask run を実行して、この段階でのアプリケーションを確認してください。

ラジオボタンを使用して、さまざまな登録オプションを表示したい場合があるでしょう。index.html を次のように改善できます。

{% extends "layout.html" %}

{% block body %}
    Register
     action="/register" method="post">
         autocomplete="off" autofocus name="name" placeholder="Name" type="text">
        {% for sport in sports %}
             name="sport" type="radio" value="{{ sport }}"> {{ sport }}
        {% endfor %}
         type="submit">Register

{% endblock %}

typeradio に変更されたことに注目してください。

  • 再度 flask run を実行すると、インターフェースがどのように変更されたかを確認できます。

多くの異なる登録者の登録を受け付けたい場合があるでしょう。app.py を次のように改善できます。

# Implements a registration form, storing registrants in a dictionary, with error messages

from flask import Flask, redirect, render_template, request

app = Flask(__name__)

REGISTRANTS = {}

SPORTS = [
    "Basketball",
    "Soccer",
    "Ultimate Frisbee"
]

@app.route("/")
def index():
    return render_template("index.html", sports=SPORTS)

@app.route("/register", methods=["POST"])
def register():

    # Validate name
    name = request.form.get("name")
    if not name:
        return render_template("error.html", message="Missing name")

    # Validate sport
    sport = request.form.get("sport")
    if not sport:
        return render_template("error.html", message="Missing sport")
    if sport not in SPORTS:
        return render_template("error.html", message="Invalid sport")

    # Remember registrant
    REGISTRANTS[name] = sport

    # Confirm registration
    return redirect("/registrants")

@app.route("/registrants")
def registrants():
    return render_template("registrants.html", registrants=REGISTRANTS)

REGISTRANTS[name] によって選択された sport を記録するために、REGISTRANTS という辞書が使用されていることに注目してください。また、registrants=REGISTRANTS がこのテンプレートに辞書を渡していることにも注目してください。

さらに、error.html を実装できます。

{% extends "layout.html" %}

{% block body %}
    Error

{{ message }}

     alt="Grumpy Cat" src="/static/cat.jpg">
{% endblock %}

さらに、registrants.html という新しいテンプレートを次のように作成します。

{% extends "layout.html" %}

{% block body %}
    Registrants

                Name
                Sport

            {% for name in registrants %}

                    {{ name }}
                    {{ registrants[name] }}

            {% endfor %}

{% endblock %}

{% for name in registrants %}...{% endfor %} が各登録者を反復処理することに注目してください。動的なWebページで反復処理ができるのは非常に強力です!

  • 最後に、app.py と同じフォルダに static というフォルダを作成します。そこに、次ののファイルをアップロードします。

  • flask run を実行して、アプリケーションを動かしてみてください。

  • これでWebアプリケーションが完成しました!しかし、いくつかのセキュリティ上の欠陥があります。すべてがクライアント側にあるため、攻撃者がHTMLを変更してWebサイトを「ハック」する可能性があります。さらに、サーバーがシャットダウンされると、このデータは保持されません。サーバーが再起動してもデータを保持し続ける方法はあるでしょうか?

FlaskとSQL

  • PythonがSQLデータベースとインターフェースする方法を見てきたように、Flask、Python、SQLの力を組み合わせて、データが永続化されるWebアプリケーションを作成できます!

  • これを実装するには、いくつかの手順を踏む必要があります。

  • まず、次の SQL データベースfroshims フォルダにダウンロードします。

  • ターミナルで sqlite3 froshims.db を実行し、.schema と入力してデータベースファイルの内容を確認します。さらに SELECT * FROM registrants; と入力して内容を確認してください。現在、ファイルには登録がないことがわかります。

次に、requirements.txt を次のように修正します。

cs50
Flask

index.html を次のように修正します。

{% extends "layout.html" %}

{% block body %}
    Register
     action="/register" method="post">
         autocomplete="off" autofocus name="name" placeholder="Name" type="text">
        {% for sport in sports %}
             name="sport" type="checkbox" value="{{ sport }}"> {{ sport }}
        {% endfor %}
         type="submit">Register

{% endblock %}

layout.html を次のように修正します。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        froshims

        {% block body %}{% endblock %}

error.html が次のように表示されることを確認してください。

{% extends "layout.html" %}

{% block body %}
    Error

{{ message }}

     alt="Grumpy Cat" src="/static/cat.jpg">
{% endblock %}

registrants.html を次のように修正します。

{% extends "layout.html" %}

{% block body %}
    Registrants

                Name
                Sport

            {% for registrant in registrants %}

                    {{ registrant.name }}
                    {{ registrant.sport }}

                         action="/deregister" method="post">
                             name="id" type="hidden" value="{{ registrant.id }}">
                             type="submit">Deregister

            {% endfor %}

{% endblock %}

隠し値 registrant.id が含まれているため、後で app.py でこの id を使用できることに注目してください。

最後に、app.py を次のように修正します。

# Implements a registration form, storing registrants in a SQLite database, with support for deregistration

from cs50 import SQL
from flask import Flask, redirect, render_template, request

app = Flask(__name__)

db = SQL("sqlite:///froshims.db")

SPORTS = [
    "Basketball",
    "Soccer",
    "Ultimate Frisbee"
]

@app.route("/")
def index():
    return render_template("index.html", sports=SPORTS)

@app.route("/deregister", methods=["POST"])
def deregister():

    # Forget registrant
    id = request.form.get("id")
    if id:
        db.execute("DELETE FROM registrants WHERE id = ?", id)
    return redirect("/registrants")

@app.route("/register", methods=["POST"])
def register():

    # Validate name
    name = request.form.get("name")
    if not name:
        return render_template("error.html", message="Missing name")

    # Validate sports
    sports = request.form.getlist("sport")
    if not sports:
        return render_template("error.html", message="Missing sport")
    for sport in sports:
        if sport not in SPORTS:
            return render_template("error.html", message="Invalid sport")

    # Remember registrant
    for sport in sports:
        db.execute("INSERT INTO registrants (name, sport) VALUES(?, ?)", name, sport)

    # Confirm registration
    return redirect("/registrants")

@app.route("/registrants")
def registrants():
    registrants = db.execute("SELECT * FROM registrants")
    return render_template("registrants.html", registrants=registrants)

cs50 ライブラリが利用されていることに注目してください。register には post メソッドのルートが含まれています。このルートは、登録フォームから取得した名前とスポーツを受け取り、SQLクエリを実行して namesportregistrants テーブルに追加します。deregister ルートは、ユーザーの id を取得し、その情報を使用してその人物の登録を解除するSQLクエリにルーティングされます。

  • flask run を実行して、結果を確認できます。

  • froshims の私たちの実装をダウンロードしたい場合は、こちらから行うことができます。

  • Flaskの詳細については、Flaskドキュメントをご覧ください。

Cookieとセッション

app.pyコントローラ と見なされます。ビュー はユーザーが見るものと見なされます。モデル は、データがどのように保存され、操作されるかです。これらは合わせて MVC(モデル、ビュー、コントローラ)と呼ばれます。

  • 前述の froshims の実装は、バックオフィスの管理者がデータベースに対して個人の追加や削除を行える管理者側の視点からは便利ですが、このコードを公開サーバーに実装するのは安全ではないことが想像できます。

  • 一つには、悪意のあるアクターが「登録解除」ボタンを押すことで、他のユーザーに代わって決定を下し、サーバーに記録されたそのユーザーの回答を事実上削除できてしまうからです。

  • GoogleのようなWebサービスは、ログイン資格情報を使用して、ユーザーが適切なデータにのみアクセスできるようにします。

  • これは、Cookie(クッキー)を使用して実装できます。Cookieは、コンピュータに保存される小さなファイルで、コンピュータがサーバーと通信し、「私はすでにログインしている認証済みユーザーです」と効果的に伝えることができるようにします。このCookieによる認証は、セッション と呼ばれます。

Cookieは次のように保存されることがあります。

GET / HTTP/2
Host: accounts.google.com
Cookie: session=value

ここでは、特定のセッションを表す特定の value と共に、session IDが保存されています。

  • 最も単純な形式として、login というフォルダを作成し、次のファイルを追加することでこれを実装できます。

まず、次のように書かれた requirements.txt というファイルを作成します。

Flask
Flask-Session

Flask に加えて、ログインセッションをサポートするために必要な Flask-Session も含まれていることに注目してください。

次に、templates フォルダに次のように表示される layout.html というファイルを作成します。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        login

        {% block body %}{% endblock %}

これは、タイトルとボディを備えた非常にシンプルなレイアウトを提供します。

3番目に、templates フォルダに index.html というファイルを次のように作成します。

{% extends "layout.html" %}

{% block body %}

    {% if name -%}
        You are logged in as {{ name }}.  href="/logout">Log out.
    {%- else -%}
        You are not logged in.  href="/login">Log in.
    {%- endif %}

{% endblock %}

このファイルは、session["name"] が存在するかどうかを確認していることに注目してください(詳細は以下の app.py で説明します)。存在する場合はウェルカムメッセージが表示されます。存在しない場合は、ログインするためのページにアクセスすることを推奨します。

4番目に、login.html というファイルを作成し、次のコードを追加します。

{% extends "layout.html" %}

{% block body %}

     action="/login" method="post">
         autocomplete="off" autofocus name="name" placeholder="Name" type="text">
         type="submit">Log In

{% endblock %}

これは基本的なログインページのレイアウトであることに注目してください。

最後に、app.py というファイルを作成し、次のようにコードを書きます。

from flask import Flask, redirect, render_template, request, session
from flask_session import Session

# Configure app
app = Flask(__name__)

# Configure session
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)

@app.route("/")
def index():
    return render_template("index.html", name=session.get("name"))

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        session["name"] = request.form.get("name")
        return redirect("/")
    return render_template("login.html")

@app.route("/logout")
def logout():
    session.clear()
    return redirect("/")

ファイルの先頭で、セッションをサポートできるように session を含む インポート が修正されていることに注目してください。最も重要なのは、login ルートと logout ルートで session["name"] がどのように使用されているかに注目することです。login ルートは提供されたログイン名を割り当て、それを session["name"] に代入します。しかし、logout ルートでは、session の値をクリアすることによってログアウトが実装されています。

  • セッションという抽象化により、特定のユーザーだけがアプリケーションの特定のデータや機能にアクセスできるようにすることができます。これにより、良い目的であれ悪い目的であれ、誰かが他のユーザーに代わって行動しないことを保証できます!

  • 必要に応じて、login私たちの実装をダウンロードできます。

  • セッションの詳細については、Flaskドキュメントをご覧ください。

ショッピングカート

  • セッションを有効にするFlaskの機能を利用する最後の例に進みます。

app.py 内の store のコードを検討しました。次のコードが示されました。

from cs50 import SQL
from flask import Flask, redirect, render_template, request, session
from flask_session import Session

# Configure app
app = Flask(__name__)

# Connect to database
db = SQL("sqlite:///store.db")

# Configure session
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)

@app.route("/")
def index():
    books = db.execute("SELECT * FROM books")
    return render_template("books.html", books=books)

@app.route("/cart", methods=["GET", "POST"])
def cart():

    # Ensure cart exists
    if "cart" not in session:
        session["cart"] = []

    # POST
    if request.method == "POST":
        book_id = request.form.get("id")
        if book_id:
            session["cart"].append(book_id)
        return redirect("/cart")

    # GET
    books = db.execute("SELECT * FROM books WHERE id IN (?)", session["cart"])
    return render_template("cart.html", books=books)

cart がリストを使用して実装されていることに注目してください。books.htmlAdd to Cart ボタンを使用して、このリストにアイテムを追加できます。このようなボタンをクリックすると post メソッドが呼び出され、アイテムの idcart に追加されます。get メソッドを呼び出してカートを表示すると、SQLが実行され、カート内の本のリストが表示されます。

また、books.html の内容も確認しました。

{% extends "layout.html" %}

{% block body %}

    Books
    {% for book in books %}
        {{ book["title"] }}
         action="/cart" method="post">
             name="id" type="hidden" value="{{ book['id'] }}">
             type="submit">Add to Cart

    {% endfor %}

{% endblock %}

これが for book in books を使用して、各本に対して Add to Cart を行う機能をどのように作成しているかに注目してください。

  • この flask 実装を動かす残りのファイルは、ソースコードで見ることができます。

ショー

app.py にある shows という事前に設計されたプログラムを見ました。

# Searches for shows using LIKE

from cs50 import SQL
from flask import Flask, render_template, request

app = Flask(__name__)

db = SQL("sqlite:///shows.db")

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

@app.route("/search")
def search():
    shows = db.execute("SELECT * FROM shows WHERE title LIKE ?", "%" + request.args.get("q") + "%")
    return render_template("search.html", shows=shows)

search ルートがどのように show を検索する方法を提供しているかに注目してください。この検索は、ユーザーによって提供されたものと LIKE (似ている)タイトルを探します。

また、index.html も確認しました。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        shows

         autocomplete="off" autofocus placeholder="Query" type="text">

            let input = document.querySelector('input');
            input.addEventListener('input', async function() {
                let response = await fetch('/search?q=' + input.value);
                let shows = await response.json();
                let html = '';
                for (let id in shows) {
                    let title = shows[id].title.replace('', '').replace('&', '&');
                    html += '' + title + '';
                }
                document.querySelector('ul').innerHTML = html;
            });

JavaScriptの script がオートコンプリートの実装を作成し、input に一致するタイトルが表示されることに注目してください。

この実装の残りのファイルは、ソースコードで見ることができます。

API

  • アプリケーションプログラムインターフェース または API は、別のサービスとインターフェースすることを可能にする一連の仕様です。たとえば、IMDBのAPIを利用して彼らのデータベースとインターフェースすることができます。サーバーからダウンロード可能な特定のタイプのデータを処理するためのAPIを統合することさえできるかもしれません。

shows を改良した app.py の改善版を見ると、次のようになっていました。

# Searches for shows using Ajax

from cs50 import SQL
from flask import Flask, render_template, request

app = Flask(__name__)

db = SQL("sqlite:///shows.db")

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

@app.route("/search")
def search():
    q = request.args.get("q")
    if q:
        shows = db.execute("SELECT * FROM shows WHERE title LIKE ? LIMIT 50", "%" + q + "%")
    else:
        shows = []
    return render_template("search.html", shows=shows)

search ルートがSQLクエリを実行することに注目してください。

search.html を見ると、非常にシンプルであることに気づくでしょう。

{% for show in shows %}
    {{ show["title"] }}
{% endfor %}

箇条書きのリストを提供していることに注目してください。

最後に、index.html を見ると、検索を強化するために AJAX コードが利用されていることに注目してください。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        shows

         autocomplete="off" autofocus placeholder="Query" type="search">

            let input = document.querySelector('input');
            input.addEventListener('input', async function() {
                let response = await fetch('/search?q=' + input.value);
                let shows = await response.text();
                document.querySelector('ul').innerHTML = shows;
            });

イベントリスナーを利用して、タイトルに一致するリストを提供するようサーバーに動的に問い合わせていることに注目してください。これにより、HTML内の ul タグを特定し、一致するリストを含めるようにWebページを動的に修正します。

JSON

JavaScript Object Notation または JSON は、キーと値を持つ辞書のテキストファイルです。これは、大量のデータを取得するための、コンピュータフレンドリーな未加工の方法です。

  • JSONは、サーバーからデータを取得するための非常に便利な方法です。

これは、一緒に確認した index.html で実際に確認できます。


 lang="en">

         name="viewport" content="initial-scale=1, width=device-width">
        shows

         autocomplete="off" autofocus placeholder="Query" type="text">

            let input = document.querySelector('input');
            input.addEventListener('input', async function() {
                let response = await fetch('/search?q=' + input.value);
                let shows = await response.json();
                let html = '';
                for (let id in shows) {
                    let title = shows[id].title.replace('', '').replace('&', '&');
                    html += '' + title + '';
                }
                document.querySelector('ul').innerHTML = html;
            });

上記は多少難解に見えるかもしれませんが、自分のWebアプリケーションでJSONをどのように実装できるかを自分で調査するための出発点となります。

さらに、app.py を調べて、JSONレスポンスがどのように取得されるかを確認しました。

# Searches for shows using Ajax with JSON

from cs50 import SQL
from flask import Flask, jsonify, render_template, request

app = Flask(__name__)

db = SQL("sqlite:///shows.db")

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

@app.route("/search")
def search():
    q = request.args.get("q")
    if q:
        shows = db.execute("SELECT * FROM shows WHERE title LIKE ? LIMIT 50", "%" + q + "%")
    else:
        shows = []
    return jsonify(shows)

jsonify を使用して、現代的なWebアプリケーションで受け入れ可能な読み取り可能な形式に結果を変換していることに注目してください。

  • 詳細は JSON ドキュメントをご覧ください。

  • 要約すると、皆さんはPython、Flask、HTML、そしてSQLを使用して、独自のWebアプリケーションを完成させる能力を手に入れました。

まとめ

このレッスンでは、Python、SQL、およびFlaskを利用してWebアプリケーションを作成する方法を学びました。具体的には、以下の内容について説明しました。

  • Flask

  • フォーム

  • テンプレート

  • リクエストメソッド

  • FlaskとSQL

  • Cookieとセッション

  • API

  • JSON

次回の最終講義は Sanders Theatre でお会いしましょう!