Django:检测未使用的模板

发布于 2025-01-04 03:36:45 字数 360 浏览 4 评论 0原文

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

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

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

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

Is there a way to detect unused templates in a Django project?

Before Django 1.3, that would have been possible with a simple string-matching function like this one. But since 1.3, there are generic class based views that automatically generate a template_name, if you don't override it (e.g. DetailView).

Also, if you override 3rd party module templates, those templates aren't used anywhere directly in your views.

Maybe it could be done by crawling all URL definitions, loading the corresponding views and getting the template_name from them?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱的十字路口 2025-01-11 03:36:45

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

当我注意到它不仅仅是 loader.get_template 时,我使用了 wrapt ,但这似乎是窍门很好。当然,让这个与产品保持 50000 公里的距离,但是......

现在,还要遵循的是,我使用单元测试和鼻子测试来驱动它,因此,如果您使用 Python 代码对模板进行了完整的分支覆盖,那么您应该能够获取大多数模板(假设我没有错过任何 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 了解有关wrapt 的更多详细信息。实际上使用起来比理解文档更容易,装饰器让我伤脑筋。

另外,为了跟踪哪些 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)

和输出(使用脚本对其进行格式化):

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)

通过包装上面的 2 个函数,它似乎捕获了 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

I was curious if you could do this by monkey patching/decorating get_template instead. I think you can, though you have to find all the template loading
functions (I have two in my example below).

I used wrapt when I noticed it went beyond just loader.get_template, but it seems to the trick just fine. Of course, keep this 50000 km away from prod, but...

Now, the thing to follow as well is that I am driving this with unittests and nosetests so, if you have full branch coverage of your template-using Python code, you should be able to get most templates (assuming I didn't miss any get_template-type functions).

in settings.py

This is the "brains" to patch get_template & co.

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)

See safely-applying-monkey-patches-in-python for more details on wrapt. Actually easier to use than to understand the docs, decorators make my brain hurt.

Also, to track which django functions were doing the actual loads I mispelled some template names on purpose in the code and in templates, ran unit tests on it and looked at the stacktraces for missing template exceptions.

This is my rather badly-written function which adds to a set and puts it into
a json output....

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)

and the ouput (with a script to format it):

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)

From wrapping the 2 functions above, it seems to have caught extends, %includes and straight get_templates, as well as list-type templates that were being used by class-based views. It even caught my dynamically generated templates which aren't even on the file system but get loaded with a custom loader.

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
若沐 2025-01-11 03:36:45

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

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')

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

It's not possible to detect unused templates for certain, even in the absence of generic views, because you can always write code like this:

get_template(any_code_you_like()).render(context)

So even prior to Django 1.3 the django-unused-templates application you linked to could only have worked for projects that respected some kind of discipline about the use of templates. (For example, always having a string literal as the template argument to functions like get_template and render_to_response.)

Loading all the views wouldn't be sufficient either: a view may use different templates under different circumstances:

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

And of course templates may not be used by views at all, but by other parts of the system (for example, e-mail messages).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文