Шаблон 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())
Он рекомендует это ленивым программистам, но указывает на некоторые накладные расходы, которые могут повлиять на производительность.
Я хотел бы знать, если некоторые из вас используют трюк локальных жителей в реальных приложениях. Вы рекомендуете это или это плохая практика?
Ответы
Ответ 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())
Если вам нужна причина производительности, точечные запросы медленнее.
Если вам нужна причина обслуживания, это меньше строк кода, еще более удобочитаемого и еще меньше ненужных структур.