Google app engine NDB Jinja2访问KeyProperty的最佳方式

Google app engine NDB Jinja2访问KeyProperty的最佳方式,google-app-engine,jinja2,google-cloud-datastore,app-engine-ndb,Google App Engine,Jinja2,Google Cloud Datastore,App Engine Ndb,我有这个型号 class Team(ndb.Model): name = ndb.StringProperty() password = ndb.StringProperty() email = ndb.StringProperty() class Offer(ndb.Model): team = ndb.KeyProperty(kind=Team) cut = ndb.StringProperty() price = ndb.IntegerP

我有这个型号

class Team(ndb.Model):
    name = ndb.StringProperty()
    password = ndb.StringProperty()
    email = ndb.StringProperty()


class Offer(ndb.Model):
    team = ndb.KeyProperty(kind=Team)
    cut = ndb.StringProperty()
    price = ndb.IntegerProperty()

class Call(ndb.Model):
    name = ndb.StringProperty()
    called_by = ndb.KeyProperty(kind=Team)
    offers = ndb.KeyProperty(kind=Offer, repeated=True)
    status = ndb.StringProperty(choices=['OPEN', 'CLOSED'], default="OPEN")
    dt = ndb.DateTimeProperty(auto_now_add=True)
我有这种看法

class MainHandler(webapp2.RequestHandler):
    def get(self):
        calls_open = Call.query(Call.status == "OPEN").fetch()
        calls_past = Call.query(Call.status == "CLOSED").fetch()
        template_values = dict(open=calls_open, past=calls_past)
        template = JINJA_ENVIRONMENT.get_template('templates/index.html')
        self.response.write(template.render(template_values))
还有这个小小的测试温度

{% for call in open %}
    <b>{{call.name}} {{call.called_by.get().name}}</b>
    {% endfor %}
{%for call in open%}
{{call.name}{{call.called_by.get().name}
{%endfor%}
现在,使用
get()
它可以完美地工作。

我的问题是:这是正确的吗? 有更好的方法吗? 就我个人而言,我觉得在模板中获取值很奇怪,我更喜欢在视图中获取它

我的想法是:

  • 创建一个新列表
    res\u open\u calls=[]
  • 对于所有的call-in调用\u open调用to\u dict()
    dict\u call=call.to\u dict()
  • 然后分配给dict_call
    dict_call['team']=call.team.get().to_dict()
  • 将对象添加到列表
    res\u open\u calls.append(dict\u call)
  • 然后返回这个刚刚生成的列表
这是我写的要点(用于修改代码)


它看起来更干净,但有点贵(必须生成第二个列表)。有什么更好/更聪明的方法吗?

OP显示的代码明显不同于他们正在使用的代码:他们将
调用的StringProperty
显示,因此调用
get
应该会崩溃,他们谈论的是
调用。团队
在他们显示的代码中不存在。。。不管怎样,我试图猜测他们到底有什么,因为我发现潜在的想法很重要

OP IMHO对Jinjia2模板中的DB操作感到不舒服是正确的,它最好只限于表示级别的问题。我假设(猜!)调用的
部分是:

class Call(ndb.Model):
    team = ndb.KeyProperty(kind=Team)
目前为OP工作的Jinja2的相关部分为:

{{{{call.team.get().name}}
因此,更好的结构可能是:

class Call(ndb.Model):
    team = ndb.KeyProperty(kind=Team)
    @property
    def team_name(self):
        return self.team.get().name
在模板中,只需
{{call.teamname}

这仍然在模板扩展期间执行DB操作,但它在Python代码方面执行,而不是在Jinja2方面执行——这比在一个只关注表示的模板中包含关于模型数据体系结构的这么多细节要好

或者,如果
调用
实例是
.put
很少且经常显示,并且其
团队
没有更改
名称
,可以说,
缓存
计算属性中的值

class Call(ndb.Model):
    team = ndb.KeyProperty(kind=Team)
    def _team_name(self):
        return self.team.get().name
    team_name = ComputedProperty(self._team_name)
但是,后一种选择是较差的(因为它涉及更多的存储空间,不节省执行时间,并使与数据存储的实际交互复杂化)除非
调用
实体的一些查询也需要查询
团队名称
(在后一种情况下,必须查询)

如果确实选择了这个选项,那么Jinjia2模板仍然会使用
{{call.teamname}}
:这暗示了为什么最好只在模板中使用与表示严格相关的逻辑——这为在Python代码端实现属性和属性留下了更多的自由度,无需更改模板。“关注点分离”是编程中的一个优秀原则

其他地方发布的代码片段表明了更高程度的复杂性,
Call
确实如图所示,但当然没有问题中反复显示的
Call.team
——而是通过
Call.offers
和每个
offer.team
进行双间接寻址。这在实体关系建模方面是有意义的,但在任何NoSQL数据库(包括GAE的数据存储)中,以代码片段所建议的基本“规范化”术语来实现可能会很困难

如果团队不更改名称,调用也不更改他们的报价列表,那么对模型进行反规范化(将通过双间接方式在代码片段中获取的技术冗余信息存储在
Call
中)可能会显示出更好的性能,例如通过结构化属性,在
调用
实体中嵌入
报价
对象的副本,并在
报价
实体中嵌入
团队
对象的副本(甚至只是团队名称)

与所有反规范化一样,这可能会为数据存储中的每个实体增加一些字节,但是可以通过最大限度地减少
fetch
时所需的数据存储访问次数来支付,具体取决于对各种实体和属性的访问模式

然而,到现在为止,我们已经远离了这个问题,这个问题是关于在模板中放置什么,在Python端放置什么。优化数据存储模式是一个单独的问题,它本身就非常值得Qs关注


总结一下我对后者的立场,Python代码与作为逻辑驻留的模板的核心问题:数据访问逻辑应该位于Python代码端,理想情况下嵌入在
模型
类中(使用
属性
进行即时访问,可能在实体构建或实体最终确定时实现非规范化);Jinjia2模板(或任何其他类型的纯表示层)应该只具有表示直接需要的逻辑,而不是数据访问(当然也不是业务逻辑).

您没有向我们展示
呼叫
实体,而是只展示
团队
报价
。我同意您希望视图中有实际的逻辑,而不是模板,但无法帮助我们了解您提供的信息,因此请编辑您的Q以提供完整的信息,谢谢!顺便说一句,这可能是最好的解决方案(取决于
调用
实体)可能是模型中的计算属性或实例方法——这有点像
ndb
风格的怪癖,但这通常是我个人的偏好:-@AlexMartelli my bad,添加模型以及我编写的代码的要点。我仍然很困惑。现在你说
调用