【问题标题】:Performing a getattr() style lookup in a django template在 django 模板中执行 getattr() 样式查找
【发布时间】:2010-10-25 02:10:12
【问题描述】:

当您事先不知道某个属性的名称时,Python 的 getattr() 方法很有用。

这个功能也可以在模板中派上用场,但我从来没有想出办法来做到这一点。是否有可以执行动态属性查找的内置标签或非内置标签?

【问题讨论】:

  • 我想知道您是否尝试在模板中做太多事情。 getattr 有时感觉就像 Python 代码中的黑魔法,所以它肯定是模板中的代码味道!

标签: python django django-templates


【解决方案1】:

我最近还不得不将此代码编写为自定义模板标签。为了处理所有查找场景,它首先执行标准属性查找,然后尝试执行字典查找,然后尝试 getitem 查找(以使列表工作),然后遵循标准找不到对象时的 Django 模板行为。

(2009 年 8 月 26 日更新,现在也可以处理列表索引查找)

# app/templatetags/getattribute.py

import re
from django import template
from django.conf import settings

numeric_test = re.compile("^\d+$")
register = template.Library()

def getattribute(value, arg):
    """Gets an attribute of an object dynamically from a string name"""

    if hasattr(value, str(arg)):
        return getattr(value, arg)
    elif hasattr(value, 'has_key') and value.has_key(arg):
        return value[arg]
    elif numeric_test.match(str(arg)) and len(value) > int(arg):
        return value[int(arg)]
    else:
        return settings.TEMPLATE_STRING_IF_INVALID

register.filter('getattribute', getattribute)

模板用法:

{% load getattribute %}
{{ object|getattribute:dynamic_string_var }}


【讨论】:

  • 我在这里遗漏了一些东西——第二个和第三个子句的目的是什么?如果hasattr(value, 'has_key'),那么您不能使用value.arg 在模板中访问它吗?同样,如果它是一个数组,value.i 将获取第 i 个元素。这些只是为了让函数处理这些多余的情况吗?
  • 这些是为了方便模仿 Django 模板的行为——当你做 {{value.arg}} 时,Django 检查它是否是对象的属性(第 1 条)、字典键(第 2 条)、列表index(第 3 条),然后默认为空字符串。所以是的,{{value|getattribute:dynamic_arg_name}} 不是纯粹的 Python 意义上的“getattribute”,但它的行为方式与正常的 Django 查找相同。
  • 你应该使用str.isdigit而不是numeric_test = re.compile("^\d+$"):timeit r.match('1'): 306 ns和timeit '1'.isdigit() 47.6 ns。
  • 并且不要使用dict.has_key,它已在Python 3.x中被删除,请改用in。
  • 我还会对结果进行“可调用”检查,并在可调用时调用它,这样您就不会显示方法名称。
【解决方案2】:

我不这么认为。但是编写custom template tag 来返回上下文字典中的属性并不难。如果您只是想返回一个字符串,请尝试以下操作:

class GetAttrNode(template.Node):
    def __init__(self, attr_name):
        self.attr_name = attr_name

    def render(self, context):
        try:
            return context[self.attr_name]
        except:
            # (better yet, return an exception here)
            return ''

@register.tag
def get_attr(parser, token):
    return GetAttrNode(token)

请注意,在您的视图中而不是在模板中执行此操作可能同样容易,除非这是您的数据中经常重复的条件。

【讨论】:

    【解决方案3】:

    我最终向相关模型添加了一个方法,并且可以像模板中的属性一样访问该方法。

    不过,我认为如果内置标签允许您动态查找属性会很棒,因为这是我们很多人在模板中经常遇到的问题。

    【讨论】:

      【解决方案4】:

      保持get和getattr的区别,

      @register.filter(name='get')
      def get(o, index):
          try:
              return o[index]
          except:
              return settings.TEMPLATE_STRING_IF_INVALID
      
      
      @register.filter(name='getattr')
      def getattrfilter(o, attr):
          try:
              return getattr(o, attr)
          except:
              return settings.TEMPLATE_STRING_IF_INVALID
      

      【讨论】:

        【解决方案5】:

        没有内置标签,不过write your own应该不会太难。

        【讨论】:

          【解决方案6】:

          那个 sn-p 拯救了我的一天,但我需要它来跨越关系,所以我将其更改为将 arg 拆分为“。”并递归获取值。 它可以在一行中完成: return getattribute(getattribute(value,str(arg).split(".")[0]),".".join(str(arg).split(".")[1:])) 但为了便于阅读,我将其保留在 4 中。 我希望有人对此有用。

          import re
          from django import template
          from django.conf import settings
          
          numeric_test = re.compile("^\d+$")
          register = template.Library()
          
          def getattribute(value, arg):
          """Gets an attribute of an object dynamically AND recursively from a string name"""
              if "." in str(arg):
                  firstarg = str(arg).split(".")[0]
                  value = getattribute(value,firstarg)
                  arg = ".".join(str(arg).split(".")[1:])
                  return getattribute(value,arg)
              if hasattr(value, str(arg)):
                  return getattr(value, arg)
              elif hasattr(value, 'has_key') and value.has_key(arg):
                  return value[arg]
              elif numeric_test.match(str(arg)) and len(value) > int(arg):
                  return value[int(arg)]
              else:
                  #return settings.TEMPLATE_STRING_IF_INVALID
                  return 'no attr.' + str(arg) + 'for:' + str(value)
          
          register.filter('getattribute', getattribute)
          

          【讨论】:

            猜你喜欢
            • 2020-10-03
            • 2015-08-05
            • 2010-09-18
            • 2020-10-26
            • 2017-02-15
            • 1970-01-01
            • 2013-08-04
            • 2015-02-25
            相关资源
            最近更新 更多