Django: обнаружение неиспользуемых шаблонов - PullRequest
1 голос
/ 10 февраля 2012

Есть ли способ обнаружить неиспользуемые шаблоны в проекте Django?

До Django 1.3 это было бы возможно с помощью простой функции сопоставления строк, такой как this . Но начиная с версии 1.3 существуют общие представления на основе классов, которые автоматически генерируют template_name, если вы не переопределите его (например, DetailView).

Кроме того, если вы переопределяете шаблоны сторонних модулей, эти шаблоны нигде не используются непосредственно в ваших представлениях.

Может быть, это можно сделать путем сканирования всех определений URL, загрузки соответствующих представлений и получения от них template_name?

Ответы [ 2 ]

1 голос
/ 18 июля 2015

Мне было бы любопытно, если бы вы могли сделать это, используя вместо этого патчи / декорирование обезьяны get_template. Я думаю, что вы можете, хотя вы должны найти все шаблоны загрузки функции (у меня есть два в моем примере ниже).

Я использовал wrapt , когда заметил, что он выходит за рамки просто loader.get_template, но, похоже, уловка просто отлично. Конечно, держите эти 50000 км подальше от прод, но ...

Теперь следует также отметить, что я управляю этим с помощью юнит-тестов и тестов носа, поэтому, если у вас есть полный охват ветвей вашего шаблона с использованием кода Python, вы сможете получить большинство шаблонов (при условии, что я этого не делал) не пропустите ни одной функции типа get_template).

in settings.py

Это "мозги" для патча 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)

См. safe-apply-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)

и вывод (со скриптом для его форматирования):

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 и direct 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
0 голосов
/ 13 февраля 2012

Невозможно точно определить неиспользуемые шаблоны, даже при отсутствии общих представлений, потому что вы всегда можете написать код, подобный этому:

get_template(any_code_you_like()).render(context)

Таким образом, даже до Django 1.3 приложение django-unused-templates, с которым вы связались, могло работать только для проектов, в которых соблюдалась какая-то дисциплина в отношении использования шаблонов. (Например, всегда иметь строковый литерал в качестве аргумента шаблона для таких функций, как get_template и render_to_response.)

Загрузка всех представлений также будет недостаточной: представление может использовать разные шаблоны при разных обстоятельствах:

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

И, конечно, шаблоны могут использоваться не только представлениями, но и другими частями системы (например, сообщениями электронной почты).

...