为 Django 模板定义 API?

Posted

技术标签:

【中文标题】为 Django 模板定义 API?【英文标题】:Define API for Django-Templates? 【发布时间】:2018-09-04 11:36:41 【问题描述】:

我认为模板可以与方法相媲美。

IPO(输入-处理-输出):

    需要一些输入。 它做了一些处理 它会输出一些东西。很可能是 html

在 Python 中,方法具有必需参数和可选参数(具有默认值)。

有没有办法为 Django 模板定义(我称之为)API?

我需要一些参数。 我希望某些参数具有默认值。

用例:客户可以通过 Web 界面编辑模板。我想告诉客户对模板的更改是否有效。

我可以渲染模板看看是否发生错误,但这不包括这种情况:

模板应呈现值“foo_needs_to_get_rendered”。

在这种情况下,通过渲染(并丢弃结果)验证模板不会显示错误。

相关:我想向编辑模板的客户显示帮助消息。我想列出上下文的所有可用变量。示例:“您可以使用这些变量:foo、bar、blue ...”

【问题讨论】:

好主意!我希望你能分享结果。 @albar 感谢您的积极反馈! ....我喜欢投票:-) 这更像是设计问题,而不是代码。因为会有不同的方式来实现这样的系统。那么您在这里有确切的查询吗? 您可以设置错误类型的默认值以在渲染时引发错误,然后捕获这些错误以向用户提供洞察力 好一个。类似于 Smart Wysiwyg 的东西? 【参考方案1】:

Django 模板首先在Context 对象(这是一种字典)中查找键。如果密钥不存在,KeyError 会被模板引擎捕获。通常,密钥将呈现为空白。

所以在我看来,如果你想绕过这种行为,你需要让一个丢失的键引发除 KeyError 之外的其他东西。或者,您可以在模板引擎之前捕获KeyError,并在重新引发之前保存该信息。

您可以通过子类化Context 来做到这一点,这需要对模板引擎代码进行一些研究......但这可能是一个非常好的方法。但是您也可以将您的 API 包装在一个执行此操作的特殊类中......并将其放在上下文中。以下代码尚未经过测试.. 将其视为伪代码,您可以将其用作起点...

# our own KeyError class that won't be caught by django
class APIKeyError(Exception):
    pass

class API(object):

  def __init__(self, context, exc_class=APIKeyError):
      self.context = context
      self.exc_class = exc_class
      self.invalid_keys = set()
      self.used_keys = set()

  @property
  def unused_keys(self):
      return set(self.context.keys()) - self.used_keys

  def __getattr__(self, name):
      try:
          value = self.context.get(name)
          self.used_keys.add(name)
          return value
      except KeyError:
          self.invalid_keys.add(name)
          if self.exc_class is None:
              raise
          raise self.exc_class(
              'API key "%s" is not valid.  Available keys are %s.' %
              (name, self.context.keys()))

那么你会像这样检查你的模板:

from django.template.loader import render_to_string

api = API(
    'foo': 'foovalue',
    'bar': 'barvalue',
    'baz': 'bazvalue',
, None)

render_to_string('template.html', 'api': api , request=request)

# now the API class instance knows something about the keys (only within
# the 'api' namespace that the template used...
print api.used_keys  # set of keys that the template accessed that were valid 
print api.invalid_keys  # set of keys the template accessed that were invalid
print api.unused_keys  # set of keys valid keys that were not used

现在,请注意,如果您不想在最后进行任何检查,但只在用户使用无效密钥时抛出异常,请不要将 None 传递为 exc_class,它会当模板中有错误的 api.key 时抛出 APIKeyError

所以,希望这会给你一些想法和一些代码的起点。正如我所说,这根本没有经过测试。把它当作伪代码。

当然,这只会保护 API 中的密钥。任何不以 api 开头的密钥对于 Django 来说都是正常的。但这里的优点是您只使用自己的代码,而不是暴露于对 Django 未来版本的更改的破坏。

【讨论】:

逻辑有问题,未将使用的键添加到列表中,我已修复。【参考方案2】:

它做 IPO(输入-处理-输出):

刚开始学Django的时候我也有同样的想法。以下是我当时每个模板的第一行。最初的目的是在我想调试模板时显示输入值,方法是删除第一行中的% comment %

# This comment is to let vim know this is django html template. #% comment %
Purpose: Generate inline add form for intercoolerjs to update single page view.
Input: show_add_form:  show_add_form , list_path:  list_path , detail_id:  detail_id % if show_add_form %, default_desc:  default_desc % else %% endif %
% comment %% endcomment %

所以对于定义 API,我认为我们可以从这个模板头之类的东西开始。然后,您的代码可以在生成的模板中查找 doc_string 和 ValueError。

% comment %
doc_string: You can use these variables: foo, bar, blue (default is yellow) ..
% comment %% endcomment %
% load my_tags %
% required foo 'foo_needs_to_get_rendered' %
% required bar 'bar_needs_to_get_rendered' %
% set_with_default blue 'yellow' as blue %

set_with_defaultrequired 是 custom tags 中的 <your app>/templatetags/my_tags.py per document。

from django import template

register = template.Library()

@register.simple_tag
def set_with_default(value, default=''):
    if value:
        pass
    else:
        return default
    return value


@register.simple_tag
def required(value, message):
    if value:
        pass
    else:
        return 'ValueError: "%s"' % message
    return ''

【讨论】:

我刚刚添加了required 标签以在变量丢失时显示消息。尚不知道如何获取值的名称以避免在消息中对其进行硬编码。

以上是关于为 Django 模板定义 API?的主要内容,如果未能解决你的问题,请参考以下文章

django模板templates详解

django自定义模板标签和过滤器

Django 模板自定义标签为布尔值

可以为 Django 中的扩展 html 模板定义一个单独的 css 文件吗?

Django 表单模板自定义

特定模型的 Django 管理自定义模板