【问题标题】:How to implement a search function using a raw SQL query如何使用原始 SQL 查询实现搜索功能
【发布时间】:2019-07-19 22:59:42
【问题描述】:

我正在创建一个由 CS50 的网络系列指导的应用程序,它要求我只使用原始 SQL 查询而不是 ORM。

我正在尝试创建一个搜索功能,用户可以在其中查找存储在数据库中的书籍列表。我希望他们能够查询名为“书籍”的表中的 ISBN、标题、作者列

目前,它确实可以毫无问题地发出“GET”请求,但它没有返回任何数据,我认为问题出在我编写的 SQL 行上。

路线如下:

@app.route("/", methods=['GET','POST'])
def index():
    # search function for books
    if request.method == "GET":
        searchQuery = request.form.get("searchQuery")
        # return value from the search
        searchResult = db.execute("SELECT isbn, author, title FROM books WHERE isbn LIKE '%"+searchQuery+"%' OR author LIKE '%"+searchQuery+"%' OR title LIKE '%"+searchQuery+"%'").fetchall()
        # add search result to the list
        session["books"] = []
        # add the each result to the list
        for i in searchResult:
            session["books"].append(i)
        return render_template("index.html", books=session["books"])
    return render_template("index.html")

这是我的模板。

    <form method="GET">
        <input type="text" name="searchQuery" class="searchTerm" placeholder="What are you looking for?">
        <button type="submit" class="searchButton">submit</button>
    </form>
    <div>
        <h3>
            {% for book in books %}
            {{ book }}
            {% endfor %}
        </h3>
    </div>

请问谁能发现问题?请注意,我应该使用原始 SQL 查询和会话。

【问题讨论】:

  • 你在使用 SQLite 吗?你在哪里打开数据库和游标对象?打印 searchResult 时会发生什么?
  • 较短的session["books"] = searchResult。或者更短的books=searchResult
  • 在屏幕上打印查询并直接在数据库中使用它(使用任何数据库查看器/编辑器)以查看您是否获得此查询的任何结果。也可以查看len(searchResult)
  • @furas 当你说 DB 编辑器时,比如在 Adminer 中?
  • 是的,可以是Adminer,也可以是DBbeaverHeidiSQL等。但是每个数据库都在console/terminal/cmd.exe中预装了访问它的程序。 MySQL 有程序mysql,SQLite 有程序sqlite3 等,因此您不必安装外部程序,但它们以文本模式工作,因此不像 GUI 程序那样用户友好。

标签: python sql flask sqlalchemy jinja2


【解决方案1】:

我为你创建了一个完整解决方案的 github :)

https://github.com/researcher2/stackoverflow_57120430

有几点:

避免 SQL 注入

我建议在执行原始 sql 语句时使用绑定,我的代码反映了这一点。在偶然发现这个之前,我花了很长时间试图让这个与你的声明一起工作:

Python SQLite parameter substitution with wildcards in LIKE

基本上您不能将绑定放在 LIKE '%?%' 中,因为引号会导致替换标记被忽略。

相反,您只需要 LIKE 吗?并手动构建替换。

使用会话

所有会话信息都经过 JSON 序列化,然后发送到客户端。在这种情况下,行记录不是 JSON 可序列化的。这对我来说是一个错误:

TypeError: Object of type 'RowProxy' is not JSON serializable

我可能不会在这里使用会话,因为客户端不需要意识到这一点,因为无论如何您都会为他们构建一个包含信息的漂亮 html 页面。只需使用 python 字典并将其传递给模板引擎。我的代码确实使用了会话,因为这是您开始使用的。

如果 github 出现故障:

from flask import request, render_template, session
from app import app, db

@app.route("/", methods=['GET','POST'])
def index():
    if request.method == "POST":
        searchQuery = request.form.get("searchQuery")
        print(searchQuery)

        # Avoid SQL Injection Using Bindings
        sql = "SELECT isbn, author, title \
               FROM book \
               WHERE isbn LIKE :x \
               OR author LIKE :y \
               OR title LIKE :z"

        # I spent an hour wondering why I couldnt put the bindings inside the wildcard string...
        # https://stackoverflow.com/questions/3105249/python-sqlite-parameter-substitution-with-wildcards-in-like
        matchString = "%{}%".format(searchQuery)

        stmt = db.text(sql).bindparams(x=matchString, y=matchString, z=matchString)

        results = db.session.execute(stmt).fetchall()
        print(results)

        session["books"] = []

        for row in results:
            # A row is not JSON serializable so we pull out the pieces
            book = dict()
            book["isbn"] = row[0]
            book["author"] = row[1]
            book["title"] = row[2]
            session["books"].append(book)
        return render_template("index.html", searchedFor=searchQuery, books=session["books"])

    return render_template("index.html")

【讨论】:

  • 您对我的问题提出了更高的要求。非常感谢。老实说,我不明白其中的 %100,但这将是一个很棒的学习材料。
  • 不客气,你相信我会觉得这很有趣吗?如果您对回购有任何疑问,请告诉我。我想你还没有看到需求文件和 sqlalchemy orm 的东西吗?
  • book = dict(row) 足以进行序列化。
【解决方案2】:

感谢您选择的答案。 一个简单的改变就解决了这个问题。

我需要在 SQL 查询行中将“LIKE”换成“ILIKE”。

searchResult = db.execute(
    """
    SELECT isbn, author, title
    FROM books
    WHERE isbn ILIKE :search OR author ILIKE :search OR title ILIKE :search
    """,
    {"search": "%"+searchQuery+"%"}
).fetchall()

这可能不是一个优雅的解决方案,但它确实解决了一个临时问题。再次感谢那个回答并为在做 CS50 的网络系列时遇到问题的其他所有人创建 github 存储库的人!

【讨论】:

  • 干得好。我是不是看错了,你没有使用烧瓶会话,只是一个名为 session 的变量?
  • @Researcher 我实际上正在使用它。 from flask import session
猜你喜欢
  • 2018-10-17
  • 2021-01-09
  • 2015-09-24
  • 2021-01-08
  • 2022-07-07
  • 2014-12-10
  • 2018-07-17
  • 1970-01-01
相关资源
最近更新 更多