Django:检测未使用的模板

时间:2012-02-10 12:06:29

标签: python django refactoring code-cleanup

有没有办法在Django项目中检测未使用的模板?

在Django 1.3之前,使用像this one之类的简单字符串匹配函数就可以实现。但是从1.3开始,有一些基于类的通用视图会自动生成template_name,如果你不覆盖它(例如DetailView)。

此外,如果您覆盖第三方模块模板,则不会直接在视图中的任何位置使用这些模板。

也许可以通过抓取所有网址定义,加载相应的视图并从中获取template_name来完成?

2 个答案:

答案 0 :(得分:1)

我很好奇你是否可以通过猴子修补/装饰get_template来做到这一点。我认为你可以,但你必须找到所有的模板加载 函数(我的例子中有两个)。

当我注意到它超出了loader.get_template时,我使用了wrapt,但似乎诀窍就好了。当然,保持距离刺激50000公里,但是......

现在,接下来要做的就是我用unittests和nosetests来驱动它,所以,如果你对你的模板有完整的分支覆盖 - 使用Python代码,你应该能够获得大多数模板(假设我没有&# 39; t miss任何get_template类型的函数。)

settings.py

中的

这是"大脑"修补get_template&共

import wrapt
import django.template.loader
import django.template.engine

def wrapper(wrapped, instance, args, kwargs):

    #concatenate the args vector into a string.
    # print "\n\n\n\n%s\nI am a wrapper \nusage:%s\n%s\n\n\n\n\n" % ("*"*80, usage, "*"*80)
    try:
        return wrapped(*args, **kwargs)
    finally:
        usage = ",".join([unicode(arg) for arg in args if arg])
        track_usage(usage)

#you have to wrap whatever is loading templates...
#imported django module + class/method/function path of what needs to be
#wrapped within that module.  comment those 2 lines out and you are back to
#normal


wrapt.wrap_function_wrapper(django.template.loader, 'get_template', wrapper)
wrapt.wrap_function_wrapper(django.template.engine, 'Engine.find_template', wrapper)

有关包裹的详细信息,请参阅safely-applying-monkey-patches-in-python。实际上比理解文档更容易使用,装饰者让我的大脑受伤。

另外,为了跟踪哪些django函数正在执行实际加载,我在代码和模板中故意错误地编写了一些模板名称,对其进行单元测试并查看堆栈跟踪以查找缺少的模板异常。

这是我写得很糟糕的函数,它添加了一个集合并将其放入 一个json输出....

def track_usage(usage):
    fnp_usage = "./usage.json"

    try:
        with open(fnp_usage, "r") as fi:
            data = fi.read()
            #read the set of used templates from the json file
            j_data = json.loads(data)
            s_used_file = set(j_data.get("li_used"))

    except (IOError,),e:
            s_used_file = set()
            j_data = dict()

    s_used_file.add(usage)
    #convert the set back to a list for json compatibility
    j_data["li_used"] = list(s_used_file)

    with open(fnp_usage, "w") as fo:
        json.dump(j_data, fo)

和ouput(用脚本来格式化):

import sys
import json
fnp_usage = sys.argv[1]


with open(fnp_usage, "r") as fi:
    data = fi.read()
    #read the set of used templates from the json file
    j_data = json.loads(data)
    li_used_file = j_data.get("li_used")
    li_used_file.sort()

    print "\n\nused templates:"
    for t in li_used_file:
        print(t)

从包装上面的两个函数开始,它似乎已经捕获了extends,%includes和直接get_templates,以及基于类的视图使用的列表类型模板。它甚至捕获了我动态生成的模板,这些模板甚至不在文件系统上,而是加载了自定义加载器。

used templates:
bootstrap/display_form.html
bootstrap/errors.html
bootstrap/field.html
bootstrap/layout/baseinput.html
bootstrap/layout/checkboxselectmultiple.html
bootstrap/layout/field_errors.html
bootstrap/layout/field_errors_block.html
bootstrap/layout/help_text.html
bootstrap/layout/help_text_and_errors.html
bootstrap/layout/radioselect.html
bootstrap/whole_uni_form.html
django_tables2/table.html
dynamic_template:db:testdb:name:pssecurity/directive.PrimaryDetails.json
uni_form/layout/div.html
uni_form/layout/fieldset.html
websec/__base.html
websec/__full12.html
websec/__l_right_sidebar.html
websec/bootstrapped_home.html
websec/changedb.html
websec/login.html
websec/requirejs_config.html
websec/topnav.html
websec/user_msg.html

答案 1 :(得分:0)

即使没有通用视图,也无法检测到未使用的模板,因为您始终可以编写如下代码:

get_template(any_code_you_like()).render(context)

因此,即使在Django 1.3之前,您链接到的django-unused-templates应用程序也只适用于那些尊重模板使用的某些规则的项目。 (例如,始终将字符串文字作为get_templaterender_to_response等函数的模板参数。)

加载所有视图也不够:视图可能在不同情况下使用不同的模板:

def my_view(request):
    if request.user.is_authenticated():
        return render(request, 'template1.html')
    else:
        return render(request, 'template2.html')

当然,视图根本不会使用模板,而是由系统的其他部分(例如,电子邮件)使用。