Week 9 Flask

欢迎!

  • 在过去的几周里,您已经学习了许多编程语言、技术和策略。

  • 事实上,这个课程远不是C 课程Python 课程,而更像是编程课程,这样您就可以继续跟踪未来的趋势。

  • 在过去的几周里,您已经学会了“如何学习”编程。

  • 今天,我们将从 HTML 和 CSS 转向组合 HTML、CSS、SQL、Python 和 JavaScript,以便您可以创建自己的 Web 应用程序。

  • 您可以考虑使用本周学到的技能来创建您的最终项目。

http-server

  • 到目前为止,您看到的所有 HTML 都是预先编写的且静态的。

  • 以前,当你访问一个页面时,浏览器会下载一个 HTML 页面,你随后就可以查看它。这类页面被认为是“静态”页面,因为 HTML 中写好的内容正是用户会看到的内容,并会被下载到用户浏览器这个“客户端”中。

  • 动态页面则可以由 Python 等语言即时生成 HTML。因此,你可以创建根据用户输入或行为,在“服务器端”通过代码生成的网页。

  • 您过去曾使用 http-server 来为您的网页提供服务。今天,我们将利用一个新的服务器,它可以解析网址并根据提供的 URL 执行操作。

此外,上周您看到的 URL 如下:

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

请注意,file.html 是 HTML 文件,位于 example.com 中名为 folder 的文件夹中。

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/路由为返回index.html的内容,参数为name。默认情况下,request.args.get 函数将查找用户提供的 name。如果未提供名称,则默认为 world@app.route 也称为装饰器。

您可以通过在终端窗口中键入 flask run 来运行此 Web 应用程序。如果 Flask 未运行,请确保上述每个文件中的语法正确。此外,如果 Flask 无法运行,请确保您的文件按如下方式组织:

/templates
    index.html
app.py
requirements.txt
  • 一旦运行,系统将提示您单击链接。导航到该网页后,尝试将 ?name=[Your Name] 添加到浏览器 URL 栏中的基本 URL 中。

表单

为了改进程序,我们要注意到,大多数用户不会直接在地址栏中输入参数。相反,程序通常会让用户在网页上填写表单。因此,我们可以将 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 已关闭。此外,输入框还包含文字为 Nameplaceholder。也请注意,我们如何使用 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 路由会将 name 传递到该网页。

要完成此实现,您将需要 templates 文件夹中的另一个 greet.html 模板,如下所示:


 lang="en">

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

        hello, {{ name }}

请注意,此路由现在将向用户呈现问候语,后跟他们的名字。

模板

  • 我们的两个网页 index.htmlgreet.html 都有很多相同的数据。允许正文是唯一的但在页面之间复制相同的布局不是很好吗?

首先新建一个名为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 %}

请注意这段代码如何更短、更紧凑。

请求方法

  • 您可以想象使用 get 不安全的场景,因为用户名和密码会显示在 URL 中。

我们可以利用方法 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"))

请注意,POST 已添加到 /greet 路由中,并且我们使用 request.form.get 而不是 request.args.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 IM 或 froshims 是一个 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")

请注意,提供了 failure 选项,如果 namesport 字段未正确填写,则会向用户显示失败消息。

接下来,通过键入 code templates/index.htmltemplates 文件夹中创建一个名为 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 %}

第四,在模板中创建一个名为 success.html 的文件,如下所示:

{% extends "layout.html" %}

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

最后,在模板中创建一个名为 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 %}

请注意 type 如何更改为 radio

  • 再次执行 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 的字典用于记录 REGISTRANTS[name] 选择的 sport。另请注意,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 %} 将遍历每个注册者。非常强大,能够在动态网页上进行迭代!

  • 最后,在与app.py相同的文件夹中创建一个名为static的文件夹。在那里,上传以下 cat 文件。

  • 执行 flask run 并运行该应用程序。

  • 您现在拥有一个网络应用程序!但是,存在一些安全缺陷!因为一切都在客户端,所以对手可以更改 HTML 并“破解”网站。此外,如果服务器关闭,这些数据将不会保留。是否有某种方法可以让我们的数据即使在服务器重新启动时也能保留?

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 库。包括用于 post 方法的 register 的路线。该路由将从注册表单中获取姓名和运动项目,并执行 SQL 查询以将 namesport 添加到 registrants 表中。 deregister 路由到 SQL 查询,该查询将获取用户的 id 并利用该信息取消此人的注册。

  • 您可以执行 flask run 并检查结果。

  • 如果您想下载我们的 froshims 实现,您可以在此处下载。

  • 您可以在 Flask 文档 中阅读有关 Flask 的更多信息。

app.py 被视为控制器视图被认为是用户看到的内容。 模型是数据存储和操作的方式。这统称为 MVC(模型、视图、控制器)。

  • 虽然从管理角度来看,froshims 的先前实现非常有用,后台管理员可以在数据库中添加和删除个人,但可以想象在公共服务器上实现此代码是多么不安全。

  • 其一,不良行为者可以通过点击取消注册按钮来代表其他用户做出决定,从而有效地从服务器中删除他们记录的答案。

  • Google 等网络服务使用登录凭据来确保用户只能访问正确的数据。

  • 我们实际上可以使用 cookies 来实现它。 Cookie 是存储在您的计算机上的小文件,以便您的计算机可以与服务器通信并有效地表明“我是已登录的授权用户”。通过此 cookie 进行的授权称为会话

Cookie 可以按如下方式存储:

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

这里,session id 与代表该会话的特定 value 一起存储。

  • 在最简单的形式中,我们可以通过创建一个名为 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 %}

请注意,这提供了一个非常简单的布局,带有标题和正文。

第三,在 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 中进一步详细说明)。如果是,它将显示一条欢迎消息。如果没有,它会建议您浏览到一个页面进行登录。

第四,创建一个名为 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,这将允许您支持会话。最重要的是,请注意 session["name"]loginlogout 路由中的使用方式。 login 路由会将提供的登录名分配给 session["name"]。而logout路由中,是通过清除session的值来实现注销的。

  • session 抽象允许您确保只有特定用户才能访问我们应用程序中的特定数据和功能。它可以让您确保没有人代表另一用户进行任何行为,无论是好是坏!

  • 如果您愿意,您可以下载 login我们的实现

  • 您可以在 Flask 文档 中阅读有关会话的更多信息。

购物车

  • 接下来是利用 Flask 的能力来启用会话的最后一个示例。

我们检查了 app.pystore 的以下代码。显示了以下代码:

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.html 中的 Add to Cart 按钮将项目添加到此列表中。单击此类按钮时,将调用 post 方法,其中该项目的 id 将附加到 cart。查看购物车时,调用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 实现的其余文件。

Shows

我们在 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 标签,并相应地修改网页以包含匹配列表。

JSON

JavaScript 对象表示法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;
            });

虽然上面的内容可能有些神秘,但它为您提供了一个自己研究 JSON 的起点,以了解如何在您自己的 Web 应用程序中实现它。

此外,我们检查了 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 见!