NDB Jinja2 访问 KeyProperty 的最佳方式
NDB Jinja2 best way to access KeyProperty
我有这个模型
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 %}
现在,与 get()
完美配合。
我的问题是:这是正确的吗?
有更好的方法吗?
我个人觉得 get()
模板中的值很奇怪,我更愿意在视图中获取它。
我的想法是:
- 创建一个新列表
res_open_calls=[]
- 对于 calls_open 中的所有调用调用 to_dict()
dict_call = call.to_dict()
- 然后赋值给dict_call
dict_call['team'] = call.team.get().to_dict()
- 将对象添加到列表中
res_open_calls.append(dict_call)
- 然后 return 这个刚刚生成的列表。
这是我写的要点(修改后的代码)https://gist.github.com/esseti/0dc0f774e1155ac63797#file-call_offers_calls
看起来更干净但有点贵(必须生成第二个列表)。有什么事 better/clever 可以做吗?
OP 显然显示的代码与他们正在使用的代码非常不同:他们将 called_by
显示为 StringProperty
因此在其上调用 get
应该会崩溃,他们说call.team
他们显示的代码中不存在...无论如何,我试图猜测他们实际拥有什么,因为我发现基本想法很重要。
恕我直言,OP 对在 Jinjia2 模板中进行数据库操作感到不安是正确的,这最好限制在表示级问题上。我假设(猜猜!)Call
模型的一部分是:
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 方面的 - 比体现模型数据的这么多细节更好模板中的架构应该只关注演示。
或者,如果 Call
实例 .put
很少但经常显示,并且它的 team
没有改变 name
,可以这么说,cache
中的值 ComputedProperty
:
class Call(ndb.Model):
team = ndb.KeyProperty(kind=Team)
def _team_name(self):
return self.team.get().name
team_name = ComputedProperty(self._team_name)
然而,后一种选择较差(因为它涉及更多存储 space,不节省执行时间,并使与数据存储的实际交互复杂化)除非一些Call
实体的查询也需要在 team_name
上查询(在后一种情况下,这是必须的)。
如果确实选择了这个选项,Jinjia2 模板将仍然 使用{{call.teamname}}
:这暗示了为什么最好在模板中仅使用与表示严格相关的逻辑-- 它为在事物的 Python 代码端实现属性和特性留下了更多的自由度,而无需更改模板。 "Separation of concerns" 是编程中的优秀原则。
其他地方发布的代码片段暗示了更高程度的复杂性,其中 Call
确实如图所示,但当然没有 call.team
问题中反复显示的 - 相反,双重通过 call.offers
和每个 offer.team
间接寻址。这在实体关系建模方面是有意义的,但在任何 NoSQL 数据库(包括 GAE 的数据存储)中建议的基本 "normalized" 术语中实施起来可能会很费力。
如果团队不更改名称,并且调用不更改他们的报价列表,非规范化 模型可能会显示更好的性能(存储在 Call
在代码段中由 运行 通过双重间接获取的技术冗余信息)——例如通过 结构化属性 、https://cloud.google.com/appengine/docs/python/ndb/properties#structured 到 在 Call
实体中嵌入 个 Offer
对象的副本,并在 [=34] 中嵌入 Team
对象(甚至只是团队名称) =]实体。
与所有去规范化一样,这可能会在数据存储中为每个实体占用一些额外的字节,但仍然可以通过最大限度地减少 fetch
时间所需的数据存储访问次数来充分支付费用,具体取决于访问各种实体和属性的模式。
但是,到目前为止,我们已经偏离了这个问题,即关于在模板中放入什么,在 Python 端放什么。优化数据存储模式是一个单独的问题,它本身就很值得问。
总结我对后者的立场,Python 代码与模板作为逻辑驻留的核心问题:数据访问逻辑应该在 Python 代码端,最好嵌入 Model
类(使用 property
进行即时访问,可能一直到实体构建或实体完成时的非规范化); Jinjia2 模板(或任何其他类型的纯表示层)应该只有表示直接需要的逻辑,而不是数据访问(当然也不是业务逻辑)。
我有这个模型
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 %}
现在,与 get()
完美配合。
我的问题是:这是正确的吗?
有更好的方法吗?
我个人觉得 get()
模板中的值很奇怪,我更愿意在视图中获取它。
我的想法是:
- 创建一个新列表
res_open_calls=[]
- 对于 calls_open 中的所有调用调用 to_dict()
dict_call = call.to_dict()
- 然后赋值给dict_call
dict_call['team'] = call.team.get().to_dict()
- 将对象添加到列表中
res_open_calls.append(dict_call)
- 然后 return 这个刚刚生成的列表。
这是我写的要点(修改后的代码)https://gist.github.com/esseti/0dc0f774e1155ac63797#file-call_offers_calls
看起来更干净但有点贵(必须生成第二个列表)。有什么事 better/clever 可以做吗?
OP 显然显示的代码与他们正在使用的代码非常不同:他们将 called_by
显示为 StringProperty
因此在其上调用 get
应该会崩溃,他们说call.team
他们显示的代码中不存在...无论如何,我试图猜测他们实际拥有什么,因为我发现基本想法很重要。
恕我直言,OP 对在 Jinjia2 模板中进行数据库操作感到不安是正确的,这最好限制在表示级问题上。我假设(猜猜!)Call
模型的一部分是:
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 方面的 - 比体现模型数据的这么多细节更好模板中的架构应该只关注演示。
或者,如果 Call
实例 .put
很少但经常显示,并且它的 team
没有改变 name
,可以这么说,cache
中的值 ComputedProperty
:
class Call(ndb.Model):
team = ndb.KeyProperty(kind=Team)
def _team_name(self):
return self.team.get().name
team_name = ComputedProperty(self._team_name)
然而,后一种选择较差(因为它涉及更多存储 space,不节省执行时间,并使与数据存储的实际交互复杂化)除非一些Call
实体的查询也需要在 team_name
上查询(在后一种情况下,这是必须的)。
如果确实选择了这个选项,Jinjia2 模板将仍然 使用{{call.teamname}}
:这暗示了为什么最好在模板中仅使用与表示严格相关的逻辑-- 它为在事物的 Python 代码端实现属性和特性留下了更多的自由度,而无需更改模板。 "Separation of concerns" 是编程中的优秀原则。
其他地方发布的代码片段暗示了更高程度的复杂性,其中 Call
确实如图所示,但当然没有 call.team
问题中反复显示的 - 相反,双重通过 call.offers
和每个 offer.team
间接寻址。这在实体关系建模方面是有意义的,但在任何 NoSQL 数据库(包括 GAE 的数据存储)中建议的基本 "normalized" 术语中实施起来可能会很费力。
如果团队不更改名称,并且调用不更改他们的报价列表,非规范化 模型可能会显示更好的性能(存储在 Call
在代码段中由 运行 通过双重间接获取的技术冗余信息)——例如通过 结构化属性 、https://cloud.google.com/appengine/docs/python/ndb/properties#structured 到 在 Call
实体中嵌入 个 Offer
对象的副本,并在 [=34] 中嵌入 Team
对象(甚至只是团队名称) =]实体。
与所有去规范化一样,这可能会在数据存储中为每个实体占用一些额外的字节,但仍然可以通过最大限度地减少 fetch
时间所需的数据存储访问次数来充分支付费用,具体取决于访问各种实体和属性的模式。
但是,到目前为止,我们已经偏离了这个问题,即关于在模板中放入什么,在 Python 端放什么。优化数据存储模式是一个单独的问题,它本身就很值得问。
总结我对后者的立场,Python 代码与模板作为逻辑驻留的核心问题:数据访问逻辑应该在 Python 代码端,最好嵌入 Model
类(使用 property
进行即时访问,可能一直到实体构建或实体完成时的非规范化); Jinjia2 模板(或任何其他类型的纯表示层)应该只有表示直接需要的逻辑,而不是数据访问(当然也不是业务逻辑)。