【问题标题】:Passing custom python object between Jinja2 and Flask在 Jinja2 和 Flask 之间传递自定义 python 对象
【发布时间】:2020-11-11 02:55:16
【问题描述】:

我是 Flask 和 Jinja2 以及 HTML 模板的新手,我正在尝试学习如何在控制器和视图之间来回传递信息。基本上,我有一个活动日历。每个事件名称都是一个超链接,它使用url_for 转到包含有关该事件的更多信息的视图。但是,尽管我可以轻松地将自定义 Event 对象列表传递到 HTML 文件中,但我不知道如何将选定的 Event 对象返回给控制器。据我所知,该对象正在变成一个字符串。下面是简化的代码。

app.py

from flask import Flask, render_template, url_for

app = Flask(__name__)

class Event(object):
    def __init__(self, name, date):
        self.name = name
        self.date = date

events = [Event('event1', '2020-04-11')]

@app.route('/')
def index():
    return render_template('index.html', events=events)


@app.route('/event/<event>')
def event(event):
    return render_template('event.html', event=event)

index.html

<!DOCTYPE html>
<html>
<body>
    {% for event in events %}
        <a href={{ url_for('event', event=event) }}>{{ event.name }}</a>
    {% endfor %}
</body>
</html>

event.html

<!DOCTYPE html>
<html>
<body>
    <p>{{ event.name }} {{ event.date }}</p>
</body>
</html>

单击该事件将我带到一个空白页面,大概是因为 event.html 正在尝试获取不存在的字符串对象的属性。
当将 python 对象传递到视图中是如此简单时,似乎有一种同样简单的方法可以从视图中取回它。请赐教!

【问题讨论】:

    标签: python html flask jinja2


    【解决方案1】:
    @app.route('/event/<event>')
    

    &lt;event&gt;,在这种情况下,是一个字符串。有关详细信息,请参阅the Flask quickstart。您不能像在此处尝试那样通过 URL 传递任意对象。

    通常,您会在此处获得某种 ID,您可以使用它来查找对象,可能在数据库中。在这种情况下,您可以将该 ID 传递给 url_for(),这将生成类似 /events/123 的 URL。

    然后,当您的 event 路由被调用时,它会在 event 参数中获得 ID。然后由您来查找正确的对象并将其传递到您的模板中。

    【讨论】:

    • 好吧,这是有道理的。我已经更改了我的代码以匹配这个逻辑并且它正在工作。出于好奇,有没有办法在模板之间传递对象?使用 url_for 以外的东西?
    • @schwartz721 不是真的。考虑一下事情是如何运作的。您的用户在他们的浏览器中输入您网站的地址,浏览器会向您的服务器发送 HTTP 请求。您的 index 路由在您的服务器上生成 HTML,然后将其发送到您用户的浏览器。您的用户单击该 HTML 中的链接,该链接会向您的服务器发送另一个 HTTP 请求以获取您的 event 路由。您的 event 路由在您的服务器上生成 HTML,然后将其发送到您用户的浏览器。
    • 所以你看,没有从一个模板直接到另一个模板的执行路径。这不是一个调用自身不同部分的单个程序。您正在生成通过用户交互在完全独立的机器上链接在一起的动态文档。这些链接由 URL 和 HTML 表单等内容构成。这就是您正在使用的数据模型——超媒体作为应用程序状态引擎 (HATEOAS)。
    • 好的,感谢您的深入回复!我肯定还在考虑这一切都是同一个程序的一部分,但你的解释是有道理的。
    【解决方案2】:

    当你收到&lt;event&gt;时,只是一个字符串,而不是一个对象。因此,您应该在数据库中找到事件 id,例如

    @app.route('/event/<event>')
    def event(event):
        e=db.find_event(event) # event is the event number or id 
        if e:
            return render_template('event.html', event=e)
        else:
            return f'Event {event} not found', 404
    

    【讨论】:

      【解决方案3】:

      这可以使用自定义 URL 转换器并提供序列化您的类的方法,但很快就会变得乏味:

      from __future__ import annotations
      import json
      
      class Event():
      
          def __init__(self, name, date):
              self.name = name
              self.date = date
      
          def to_url(self):
              name = self.name.replace(' ', '+')
              # format the date similarly
              return f'name+{name}+date+{self.date}'
      
          @classmethod
          def from_url(cls, url: str):
              # extract name and date from url ...
              event = Event(name, date)
      

      然后你编写转换器如下:

      from werkzeug.routing import BaseConverter
      from app.models.event import Event
      
      class EventConverter(BaseConverter):
      
          def to_python(self, url):
              return Event.from_url(url)
      
          def to_url(self, event):
              return event.to_url()
      

      然后在创建应用程序时声明它:

      from app.converters.event_converter import EventConverter
      
      # etc.
      app.url_map.converters['event'] = EventConverter # add URL converters.
      

      并将其用于您的路线,例如:

      @app.route('/event/<event:event>')
      def event(event):
          pass
      

      请注意,这很好,我以前曾将它用于可以仅存储在内存中的较小项目,但最好使用 ID 并将事件存储在数据库中。所以假设你有一个应用工厂:

      from flask_sqlalchemy import SQLAlchemy
      db = SQLAlchemy()
      def create_app():
          app = Flask(__name__)
          db.init_app(app)
          # etc.
      

      您将访问您的数据库并定义一个模型,如下所示:

      from app import db
      
      class Event(db.Model)
          id = db.Column(db.Integer, primary_key=True)
          name = db.Column(db.String, nullable=False)
          date = db.Column(db.DateTime, nullable=False)
      

      并传递&lt;event_id&gt;,如https://stackoverflow.com/a/61154232/5058116 中所述。然后您可以按如下方式查询Event 表/模型:

      from app.models.event import Event
      
      @app.route('/event/<int:event_id>')
      def event(event_id):
          event = Event.query.get(event_id)
          # etc.
      

      【讨论】:

        猜你喜欢
        • 2011-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多