【问题标题】:Flask redirect with data带有数据的烧瓶重定向
【发布时间】:2022-01-24 23:43:10
【问题描述】:

我有一个带有 GET 处理程序的 Flask 应用程序,该程序将配方 ID 作为 URL 参数,它从数据库中检索配方,然后呈现它:

@app.route('/recipe/<int:id>', methods=['GET'])
def get(id):
    recipe = get_recipe_from_db(id)
    return render_template('recipe.html', recipe=recipe)

这会产生一个类似/recipe/5 的网址。我希望配方标题成为结果 URL 的一部分,而不是只显示 URL 中的 id,例如recipe/5/lemon-cake。在第一个请求中,只有 id 是已知的。

我不确定这样做的好方法是什么。到目前为止,我想出了以下几点:

@app.route('/recipe/<int:id>', methods=['GET'])
def get(id):
    recipe = get_recipe_from_db(id)
    return redirect(url_for('get_with_title', id=id, title=urlify(recipe.title)))

@app.route('/recipe/<int:id>/<title>', methods=['GET'])
def get_with_title(id, title=None):
    recipe = get_recipe_from_db(id)
    return render_template('recipe.html', recipe=recipe)

这可行(即,当用户访问 /recipe/5 时,它会重定向到 /recipe/5/lemon-cake),但会遇到从数据库中检索到相同配方两次的事实。

有没有更好的方法来解决这个问题?

注意:recipe 对象很大,包含多个字段,我不想不必要地通过网络传递它。

【问题讨论】:

    标签: python python-3.x flask


    【解决方案1】:

    选项 1

    最简单的解决方案是在收到响应后立即在客户端修改 URL;因此,不需要重定向和/或查询数据库两次。这可以通过使用history.pushState()history.replaceState() 来实现。

    客户端

    <!DOCTYPE html>
    <html>
       <head>
          <script>
             function modify_url() {
               var title = {{recipe.title|tojson}};
               var id = {{recipe.id|tojson}};
               window.history.pushState('', '', id + "/" + title);
             }
          </script>
       </head>
       <h2>Recipe for {{recipe.title}}</h2>
       <body onload="modify_url()"></body>
    </html>
    

    服务器端

    @app.route('/recipe/<int:id>', methods=['GET'])
    def get(id):
        recipe = get_recipe_from_db(id)
        return render_template('recipe.html', recipe=recipe)
    

    您可能还想保留get_with_title() 路由,以防用户收藏/共享 URL(包括标题)并希望它可以访问(否则,访问时会返回“未找到”错误它)。

    选项 2

    如果您不希望每次有新请求到达时都查询数据库 - 甚至不需要检索配方条目的 title(不是每一列) - 并且您有足够的内存来保存数据,我建议在启动时查询一次数据库(仅选择idtitle)并创建一个字典,以便您可以从配方id 中快速查找title。请注意,通过这种方式,每次对表执行 INSERT/DELETE/etc 操作时,都必须相应地更新该字典。因此,如果您经常对该表进行此类操作,则可能不是解决问题的最佳方法,最好继续查询该表以检索title

    recipes = dict((row[0], row[1]) for row in result) # where row[0] is id and row[1] is title
    

    然后在您的端点中:

    @app.route('/recipe/<int:id>', methods=['GET'])
    def get(id):
        title = recipes.get(id)
        return redirect(url_for('get_with_title', id=id, title=urlify(title)))
    

    【讨论】:

      【解决方案2】:

      如果您希望“食谱标题”出现在 URL 中,则您的解决方案不适合。您应该在“请求”之前执行此操作。可能有一个 HTML 页面,您可以在其中提交食谱数据以列出所有食谱。

      在您的 HTML 页面中尝试以下操作:

      <ul> 
      {% for recipe  in recipes %} 
      <li><a href="/postdetail/{{recipe.id}}/{{recipe.title}}"></li>
       {% endfor %} 
      </ul>
      

      之后,当您单击该食谱时,您还可以看到标题。

      【讨论】:

        【解决方案3】:

        您只能查看使用 javascript 更改 URL,如 here 所示。这样,您就不会进行任何重定向或重新加载。

        如果你不想弄乱 javascript,让我们考虑一下后端解决方案:

        是否从数据库加载两次显着的性能开销值得一个复杂的解决方案?如果没有,您的解决方案很好。

        如果你真的想避免加载整个 recipe,你可以在 get 方法中只加载标题,而不是整个对象。
        您可以编写自定义 get_recipe_title(id) (大致)SELECT title FROM recipes WHERE id={id}
        如果使用 SQLAlchemy,您可能会使用 load_only - here

        【讨论】:

          【解决方案4】:

          只需将加载的配方作为参数传递:

          @app.route('/recipe/<int:id>', methods=['GET'])
          def get(id):
              recipe = get_recipe_from_db(id)
              return redirect(
                  url_for(
                      'get_with_title',
                      id=id,
                      title=urlify(recipe.title),
                      recipe=recipe,
                  ))
          
          @app.route('/recipe/<int:id>/<title>', methods=['GET'])
          def get_with_title(id, title=None, recipe=None):
              if recipe is None:
                  recipe = get_recipe_from_db(id)
              return render_template('recipe.html', recipe=recipe)
          

          【讨论】:

          • 这不起作用,它将配方序列化并将其作为查询参数传递。
          猜你喜欢
          • 2023-03-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多