Подтвердить что ты не робот

Шаблон Django и трюк локальных жителей

Книги django дают локальный трюк, чтобы избежать ввода длинного списка параметров в качестве контекстного словаря

http://www.djangobook.com/en/2.0/chapter04/

Пример:

def current_datetime(request):
    dt_now = datetime.datetime.now()
    return render_to_response('current.html', {'dt_now': dt_now})

становится:

def current_datetime(request):
    dt_now = datetime.datetime.now()
    return render_to_response('current.html', locals())

Он рекомендует это ленивым программистам, но указывает на некоторые накладные расходы, которые могут повлиять на производительность.

Я хотел бы знать, если некоторые из вас используют трюк локальных жителей в реальных приложениях. Вы рекомендуете это или это плохая практика?

4b9b3361

Ответ 1

Мне не нравится повторение - я думаю, что "DRY", "Do not Repeat Yourself", является ключевым принципом программирования. Как следствие, я действительно использовал locals() в подобных ситуациях. Отметка Django-шаблона далека от единственной ситуации такого рода: общий случай - это "функция или оператор, который принимает dict, но не против, если dict содержит дополнительные записи". (Например, обычным форматированием строк в Python является другой такой случай).

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

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

Как следствие, в дополнение к второстепенной проблеме производительности, существует сильное давление на использование locals() в "серьезном", "производственном" кодовом коде, который требует долговременного обслуживания и, следовательно, простой рефакторинг и локальность. Поэтому, когда я "программирую как можно лучше, я знаю как", а не "режущие углы", я знаю, что лучше избегать locals().

Значения, которые вы хотите иметь в контексте, в котором визуализируется шаблон, не обязательно "естественно" доступны как локальные голые имена; возможно, некоторые или многие из них являются результатами вычислений, элементами из списков или словарей и т.п. В этом случае соблазн "разрезать углы" с помощью locals() легче избежать, если вы просто скопируете эти значения в подходящий словарь, а не назначаете им локальные голые имена.

Это не самый простой компромисс, потому что неизбежно сталкиваются два хороших принципа (избегая повторения и наличия хорошей локальности) - поэтому, хороший вопрос! И не один полностью восприимчив к острым черным или белым ответам, поэтому я попытался расширить с обеих сторон. В конце концов, я думаю, что это один из тех аспектов стиля, в котором команде разработчиков может быть рекомендовано принять единообразную директиву по стилю и придерживаться ее - по крайней мере, это устраняет необходимость принимать решение заново когда возникает проблема, и создает более однородную (и, следовательно, поддерживаемую) базу кода. [[Я должен признаться, что этот конкретный момент никогда не был явно рассмотрен в руководстве по стилю команд, в которых я был, хотя, хотя многие другие имеют! -)]]

Ответ 2

Я часто думал о том, чтобы делать следующее, но я не уверен, действительно ли это полезно.

class MyStruct(object):
     pass

def my_view(request, id):
    c = MyStruct()
    c.customer = ..
    c.invoice = ..
    c.date = ..
    return render_to_response('xxx,html',c.__dict__)

Ответ 3

Мне это не нравится, лично. Вероятно, нет никаких оснований для моих предпочтений, кроме старого изречения Питона "Явное лучше, чем неявное". Мне нравится точно знать, что входит в мои шаблоны.

Ответ 4

Я использовал его без каких-либо проблем (до сих пор!).

Я не особенно люблю печатать, поэтому мне это нравится. Код, например

'customer' : customer,
'invoice' : invoice,
'date' : date

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

Ответ 5

Я думаю, это зависит от того, сколько локальных переменных вы определяете в своей функции.

Если он точно совпадает с номером, который вы хотите вернуть в свой шаблон, или "дополнительные" переменные являются простыми структурами, такими как целые или логические, тогда я думаю, что нет смысла их явно возвращать, поскольку для этого требуется больше работы.

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

Ответ 6

Я знаю, что это старый поток... В настоящее время render_to_response устарел. Используйте render вместо locals(). Прохождение вокруг всех локальных жителей - плохая практика. Вот пример views.py:

from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def mybooks(request):
    entries = Book.objects.all()
    return render(request, 'mybooks.html', {'entries': entries})

Ответ 7

Чтобы уменьшить беспорядок в views.py при сохранении явного: В controllers.py:

import sys

def auto_context(the_locals=None):
    # Take any variable in the locals() whose name ends with an underscore, and
    # put it in a dictionary with the underscore removed.

    if the_locals is None:
        # We can access the locals of the caller function even if they
        # aren't passed in.
        caller = sys._getframe(1)
        the_locals = caller.f_locals

    return dict([
        (key[:-1], value)
        for (key, value) in the_locals.items()
        if key[-1] == "_"])

В views.py:

from app.controllers import auto_context

def a_view(request): 
    hello_ = "World" # This will go into the context.
    goodnight = "Moon" # This won't.
    return render(request, "template.html", auto_context())

В template.html используйте {{ hello }}.

Вы вряд ли случайно дадите переменной имя, заканчивающееся подчеркиванием. Таким образом, вы будете точно знать, что входит в шаблон. Используйте auto_context() или эквивалентно auto_context(locals()). Наслаждайтесь!

Ответ 8

Я согласен с Алексом. Не вижу смысла создавать экземпляр класса (как предложено niels), когда вы можете просто сделать это:

def my_view(request, id):
    customer = ..
    invoice = ..
    date = ..
    return render_to_response('xxx,html', locals())

Если вам нужна причина производительности, точечные запросы медленнее.

Если вам нужна причина обслуживания, это меньше строк кода, еще более удобочитаемого и еще меньше ненужных структур.