Могу ли я получить доступ к константам в settings.py из шаблонов в Django?

367

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

{{CONSTANT_NAME}}

но это не похоже на работу. Это возможно?

Пол Уикс
источник
Если вы ищете, как передать параметр каждому ответу, посмотрите ответ bchunn о контекстных процессорах
Zags
1
Ответ от @jkbrzt - это готовое решение, которое быстро и легко решает эту проблему. Будущие читатели должны взглянуть на этот stackoverflow.com/a/25841039/396005 за принятый ответ
Брон Дэвис

Ответы:

183

Django предоставляет доступ к некоторым часто используемым константам настроек шаблона, таким как settings.MEDIA_URLи некоторые языковые настройки, если вы используете встроенные общие представления django или передаете аргумент ключевого слова экземпляра контекста в render_to_responseфункции ярлыка. Вот пример каждого случая:

from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.generic.simple import direct_to_template

def my_generic_view(request, template='my_template.html'):
    return direct_to_template(request, template)

def more_custom_view(request, template='my_template.html'):
    return render_to_response(template, {}, context_instance=RequestContext(request))

Оба этих представления будут иметь несколько часто используемых настроек, например, settings.MEDIA_URLдоступных для шаблона {{ MEDIA_URL }}, и т. Д.

Если вы ищете доступ к другим константам в настройках, просто распакуйте нужные константы и добавьте их в контекстный словарь, который вы используете в своей функции представления, например так:

from django.conf import settings
from django.shortcuts import render_to_response

def my_view_function(request, template='my_template.html'):
    context = {'favorite_color': settings.FAVORITE_COLOR}
    return render_to_response(template, context)

Теперь вы можете получить доступ settings.FAVORITE_COLORк вашему шаблону как {{ favorite_color }}.

prairiedogg
источник
66
Стоит отметить, что конкретные значения, добавляемые с использованием RequestContext, зависят от значения TEMPLATE_CONTEXT_PROCESSORS. Таким образом, если вы хотите, чтобы дополнительные значения передавались повсюду, просто напишите свой собственный процессор контекста и добавьте его в TEMPLATE_CONTEXT_PROCESSORS.
Карл Мейер
Точка согласованности в общих представлениях и во многих основных и дополнительных приложениях, дополнительный контекст называется extra_context, и очень часто он включается в аргументы представления.
Совут
Msgstr "Django предоставляет доступ к некоторым часто используемым константам настроек шаблона, таким как settings.MEDIA_URL". Это не работает в Django 1.3, хотя я, вероятно, использую его неправильно. Есть ли документация для этой функции?
SystemParadox
1
@asofyan да, добавьте, создайте собственный обработчик контекста шаблона и добавьте в TEMPLATE_CONTEXT_PROCESSORS в settings.py.
Паоло
14
Смотрите, django-settings-exportчтобы избежать необходимости писать этот код в каждом представлении.
Qris
441

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

Вот как:

  1. Создайте context_processors.pyфайл в каталоге вашего приложения. Допустим, я хочу иметь ADMIN_PREFIX_VALUEзначение в каждом контексте:

    from django.conf import settings # import the settings file
    
    def admin_media(request):
        # return the value you want as a dictionnary. you may add multiple values in there.
        return {'ADMIN_MEDIA_URL': settings.ADMIN_MEDIA_PREFIX}
  2. добавьте ваш контекстный процессор в ваш файл settings.py :

    TEMPLATES = [{
        # whatever comes before
        'OPTIONS': {
            'context_processors': [
                # whatever comes before
                "your_app.context_processors.admin_media",
            ],
        }
    }]
  3. Используйте RequestContextв вашем представлении, чтобы добавить ваши контекстные процессоры в ваш шаблон. renderЯрлык делает это автоматически:

    from django.shortcuts import render
    
    def my_view(request):
        return render(request, "index.html")
  4. и, наконец, в вашем шаблоне:

    ...
    <a href="{{ ADMIN_MEDIA_URL }}">path to admin media</a>
    ...
bchhun
источник
32
@MarkEssel Эти обручи сделаны так, чтобы переменная была доступна в каждом представлении, которое вы создадите, если оно использует функцию RequestContext. Вы всегда можете получить переменную настроек вручную в каждом представлении. Я бы выбрал многоразовый процессор контекста в любое время, вместо того, чтобы просто копировать и вставлять.
bchhun
5
делаю все возможное, чтобы избежать копирования / вставки везде, где это возможно. Будет ли каждое приложение (в рамках проекта) потребовать context_processor.py, есть ли способ создать один context_processor для всех из них?
Марк Эссель
10
@bchhun Я только что протестировал (Django 1.3): совместное использование контекстного процессора между приложениями работает нормально. :-) Я положил context_process.pyрядом с моим settings.pyфайлом и добавил "context_processors.admin_media"в свой TEMPLATE_CONTEXT_PROCESSORSсписок. Кроме того, вы можете добавить примечание в своем ответе о том факте, что значение по умолчанию TEMPLATE_CONTEXT_PROCESSORS не является пустым, поэтому, если какой-либо из существующих кодов использует любое из значений, установленных этими процессорами контекста по умолчанию, они не будут работать, если вы не добавите их обратно. в список явно.
MiniQuark
5
@MarkEssel Совсем не больно - он просто все прописал. Это всего лишь 6 коротких строк (шаги 1 и 2). Шаги 3 и 4 или их эквиваленты требуются для большинства шаблонов в любом случае.
Рик Вестера
2
Начиная с Django 1.3, вы можете использовать renderярлык, чтобы избежать необходимости явно включать RequestContext: docs.djangoproject.com/en/1.6/topics/http/shortcuts/#render
yndolok
269

Я считаю, что самым простым подходом является использование одного пользовательского тега шаблона :

from django import template
from django.conf import settings

register = template.Library()

# settings value
@register.simple_tag
def settings_value(name):
    return getattr(settings, name, "")

Применение:

{% settings_value "LANGUAGE_CODE" %}
Берислав Лопач
источник
17
Мне нравится иметь доступ по требованию к любому параметру в шаблонах, и это обеспечивает это элегантно. Это действительно намного лучше, чем другие ответы, если вы часто будете использовать различные настройки в своих шаблонах: 1) Принятый ответ несовместим или не соответствует представлениям на основе классов. 2) При использовании решения для процессора контекста с избыточным голосованием вам придется указывать отдельные параметры (или все), и оно будет выполняться для каждого отдельного запроса, который отображает шаблон - неэффективно! 3) Это проще, чем более сложный тег выше.
Бен Робертс
16
@ BenRoberts Я согласен, что это элегантное решение ... но только для крошечных проектов с одним разработчиком, который делает все. Если у вас есть отдельные люди / команды для проектирования и разработки, то это решение, вероятно, худшее . Что мешает дизайнеру злоупотреблять этим тегом, например {% settings_value "DATABASES" %}:? Этот вариант использования должен прояснить, почему настройки не доступны в шаблонах для начала.
mkoistinen
23
«Мы все здесь взрослые по обоюдному согласию»
14:49
11
Простите меня за то, что я новичок. Где вы положили этот код? Views.py? Или на новый файл?
Ноэль Льеварес
13
чтобы быть понятным для других людей, вам необходимо: 1) создать templatetagsпапку внутри вашего приложения с пустым __init__.pyфайлом и этим кодом, как settings.pyвнутри этой папки. 2) в свой шаблон вы добавляете {% load settings %}и затем используете свой новый тег!
Дамио
95

Проверьте django-settings-export(отказ от ответственности: я автор этого проекта).

Например...

$ pip install django-settings-export

settings.py

TEMPLATES = [
    {
        'OPTIONS': {
            'context_processors': [
                'django_settings_export.settings_export',
            ],
        },
    },
]

MY_CHEESE = 'Camembert';

SETTINGS_EXPORT = [
    'MY_CHEESE',
]

template.html

<script>var MY_CHEESE = '{{ settings.MY_CHEESE }}';</script>
Якуб Розтоцил
источник
1
И обратите внимание, что в ваших взглядах нужно использовать, renderа неrender_to_response
Everett
У меня есть подобное требование для чтения значений из настроек в шаблонах, но я получаю 500 ошибок, когда я добавляю 'django_settings_export.settings_export' в файл настроек. Можете ли вы предположить, что я делаю здесь неправильно
Пиюш Саху
3
Сейчас 2019 год, и я использую его в своем проекте. Спасибо!
Сивабуд
1
Я согласен с @sivabudh. Для меня это также лучшее решение, потому что: 1. Централизованное, что означает, что мне не нужны дополнительные папки и файлы, 2. Я могу видеть пространство имен настроек в своем шаблоне, что очень полезно для получения ссылок на многие приложения.
ywiyogo
46

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

@register.tag
def value_from_settings(parser, token):
    try:
        # split_contents() knows not to split quoted strings.
        tag_name, var = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents.split()[0]
    return ValueFromSettings(var)

class ValueFromSettings(template.Node):
    def __init__(self, var):
        self.arg = template.Variable(var)
    def render(self, context):        
        return settings.__getattr__(str(self.arg))

Затем вы можете использовать:

{% value_from_settings "FQDN" %}

распечатать его на любой странице, не перепрыгивая через контекстные процессоры.

fadedbee
источник
6
я думаю, что это самое элегантное решение, так как оно работает как dropin без изменения кода.
летающие овцы
1
что вы можете оставить остальную часть своего приложения без изменений: вы добавляете один тег и используете его, вместо того, чтобы добавлять контекстные процессоры (что означает, что вы должны редактировать свое приложение в нескольких местах)
овца
2
@Mark - в produi / src / produi / template_utils / templatetags / custom_template_filters.py на template_utils ссылаются из settings.py INSTALLED_APPS - также см. Docs.djangoproject.com/en/dev/howto/custom-template-tags
fadedbee
оцените помощь chris, добавили приложение mutil с подкаталогом templatetags, включая custom_template_filters. По-прежнему получаю сообщение об ошибке в homepage.html «Недопустимый тег блока:« value_from_settings », ожидаемый« конечный блок »или« баннер конечного блока »»
Марк Эссель
Я думаю, что это идет вразрез с «явным лучше, чем неявным», используя версию декоратора контекста, вы выбираете, какие именно настройки выставлять.
SJH
29

Мне нравится решение Берислава, потому что на простых сайтах оно чисто и эффективно. Что мне НЕ нравится, так это выставление всех констант настроек волей-неволей. Итак, что я в итоге сделал это:

from django import template
from django.conf import settings

register = template.Library()

ALLOWABLE_VALUES = ("CONSTANT_NAME_1", "CONSTANT_NAME_2",)

# settings value
@register.simple_tag
def settings_value(name):
    if name in ALLOWABLE_VALUES:
        return getattr(settings, name, '')
    return ''

Применение:

{% settings_value "CONSTANT_NAME_1" %}

Это защищает любые константы, которые вы не назвали, от использования в шаблоне, и, если вы хотите получить действительно модный, вы можете установить кортеж в настройках и создать более одного тега шаблона для разных страниц, приложений или областей, и просто при необходимости объедините локальный кортеж с кортежем настроек, затем выполните понимание списка, чтобы увидеть, является ли значение приемлемым.
Я согласен, на сложном сайте это немного упрощенно, но есть значения, которые было бы неплохо иметь универсально в шаблонах, и это, кажется, работает хорошо. Спасибо Бериславу за оригинальную идею!

MontyThreeCard
источник
5
почему бы простоif name in ALLOWABLE_VALUES: ...
пнр
Потому что я думал, что я умен, и хотел, чтобы подстроки не запускали настройки var. ;-) Возвращение, вероятно, должно быть таким: return getattr (settings, is_allowable, '')
MontyThreeCard
5
Просто чтобы прояснить для тех , кто никогда интересно: 'val' in ('val_first', 'second_val',)это False, не подстрок проблемы.
frnhr
2
Как я могу использовать это в ifзаявлении? Я хочу проверить DEBUGзначение
AJ
Если кому-то понадобится версия с повторным включением gist.github.com/BrnoPCmaniak/632f56ddb907108b3d43fa862510dfca
Филип Добровольный
12

Я немного улучшил ответ chrisdew (чтобы создать свой собственный тег).

Сначала создайте файл, yourapp/templatetags/value_from_settings.pyв котором вы определите свой новый тег value_from_settings:

from django.template import TemplateSyntaxError, Variable, Node, Variable, Library
from yourapp import settings

register = Library()
# I found some tricks in URLNode and url from defaulttags.py:
# https://code.djangoproject.com/browser/django/trunk/django/template/defaulttags.py
@register.tag
def value_from_settings(parser, token):
  bits = token.split_contents()
  if len(bits) < 2:
    raise TemplateSyntaxError("'%s' takes at least one " \
      "argument (settings constant to retrieve)" % bits[0])
  settingsvar = bits[1]
  settingsvar = settingsvar[1:-1] if settingsvar[0] == '"' else settingsvar
  asvar = None
  bits = bits[2:]
  if len(bits) >= 2 and bits[-2] == 'as':
    asvar = bits[-1]
    bits = bits[:-2]
  if len(bits):
    raise TemplateSyntaxError("'value_from_settings' didn't recognise " \
      "the arguments '%s'" % ", ".join(bits))
  return ValueFromSettings(settingsvar, asvar)

class ValueFromSettings(Node):
  def __init__(self, settingsvar, asvar):
    self.arg = Variable(settingsvar)
    self.asvar = asvar
  def render(self, context):
    ret_val = getattr(settings,str(self.arg))
    if self.asvar:
      context[self.asvar] = ret_val
      return ''
    else:
      return ret_val

Вы можете использовать этот тег в своем шаблоне через:

{% load value_from_settings %}
[...]
{% value_from_settings "FQDN" %}

или через

{% load value_from_settings %}
[...]
{% value_from_settings "FQDN" as my_fqdn %}

Преимущество as ...обозначений состоит в том, что это облегчает использование в blocktransблоках с помощью простого {{my_fqdn}}.

pklaus
источник
12

Добавление ответа с подробными инструкциями по созданию настраиваемого тега шаблона, решающего эту проблему, с помощью Django 2.0+

В вашей папке приложения создайте папку с именем templatetags . В нем создайте __init__.py и custom_tags.py :

Структура папок пользовательских тегов

В custom_tags.py создайте функцию пользовательского тега, которая обеспечивает доступ к произвольному ключу в константе настроек :

from django import template
from django.conf import settings

register = template.Library()

@register.simple_tag
def get_setting(name):
    return getattr(settings, name, "")

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

Затем вам нужно сообщить Django об этом (и любом дополнительном) пользовательском теге, загрузив этот файл в любой шаблон, где вы будете его использовать. Так же, как вам нужно загрузить встроенный статический тег:

{% load custom_tags %}

Когда он загружен, его можно использовать так же, как и любой другой тег, просто укажите нужную настройку, которую вам нужно вернуть. Так что если у вас есть переменная BUILD_VERSION в ваших настройках:

{% get_setting "BUILD_VERSION" %}

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

Примечание. Более чистым и отказоустойчивым решением, вероятно, было бы создание настраиваемого контекстного процессора, в котором вы добавляете нужные настройки в контекст, доступный для всех шаблонов. Таким образом, вы по ошибке уменьшите риск вывода конфиденциальных настроек в ваши шаблоны.

Андреас Бергстрем
источник
9

Добавьте этот код в файл с именем context_processors.py:

from django.conf import settings as django_settings


def settings(request):
    return {
        'settings': django_settings,
    }

А затем, в вашем файле настроек, включите путь, такой как 'speedy.core.base.context_processors.settings'(с вашим именем приложения и пути) в 'context_processors'настройках в TEMPLATES.

(Вы можете увидеть, например, settings / base.py и context_processors.py ).

Затем вы можете использовать определенные настройки в любом коде шаблона. Например:

{% if settings.SITE_ID == settings.SPEEDY_MATCH_SITE_ID %}

Обновление: приведенный выше код предоставляет все настройки для шаблонов, включая конфиденциальную информацию, такую ​​как ваша SECRET_KEY. Хакер может использовать эту функцию для отображения такой информации в шаблонах. Если вы хотите предоставить шаблонам только определенные настройки, используйте этот код:

def settings(request):
    settings_in_templates = {}
    for attr in ["SITE_ID", ...]: # Write here the settings you want to expose to the templates.
        if (hasattr(django_settings, attr)):
            settings_in_templates[attr] = getattr(django_settings, attr)
    return {
        'settings': settings_in_templates,
    }
Скоростной матч
источник
1
Я столкнулся с этой проблемой вчера, нашел этот пост, затем еще 2 и пост в блоге и почувствовал, что каждый из них был слишком сложным (к сожалению, я не делал это так далеко внизу, позор мне). Итак, я закончил тем, что катал свое собственное, и это именно то решение. Я только что вернулся, потому что меня беспокоило то, что люди рекомендовали плагины и целый лот-код, когда эта ^^^ 3-строчная функция и 1-строчное изменение в settings.py.
ДХМ
@DXM Спасибо!
Скоростной матч
На самом деле мое решение предоставляет все настройки шаблонов, в том числе конфиденциальную информацию, такую ​​как SECRET_KEY. Хакер может использовать эту функцию для отображения такой информации в шаблонах.
Скоростной матч
Я обновил свой ответ.
Скоростной матч
ну ... отлично, теперь у моего сайта такая же проблема :) Но ... я могу что-то упустить, однако мы уверены, что есть проблема? Шаблоны по сути такие же, как исходный код для вашего сайта, не так ли? Они хранятся на стороне сервера и недоступны непосредственно из внешнего интерфейса. Если хакер может изменить шаблон, он может изменить любой файл .py.
ДХМ
8

Приведенный выше пример из bchhun хорош, за исключением того, что вам нужно явно создать свой контекстный словарь из settings.py. Ниже приведен НЕПРОВЕРЕННЫЙ пример того, как вы можете автоматически построить контекстный словарь из всех прописных атрибутов settings.py (re: "^ [A-Z0-9 _] + $").

В конце settings.py:

_context = {} 
local_context = locals()
for (k,v) in local_context.items():
    if re.search('^[A-Z0-9_]+$',k):
        _context[k] = str(v)

def settings_context(context):
    return _context

TEMPLATE_CONTEXT_PROCESSORS = (
...
'myproject.settings.settings_context',
...
)
IanSR
источник
8

Если кто-то найдет этот вопрос, как я, я опубликую свое решение, которое работает на Django 2.0:

Этот тег назначает некоторое значение переменной settings.py переменной шаблона:

Применение: {% get_settings_value template_var "SETTINGS_VAR" %}

приложение / templatetags / my_custom_tags.py:

from django import template
from django.conf import settings

register = template.Library()

class AssignNode(template.Node):
    def __init__(self, name, value):
        self.name = name
        self.value = value

    def render(self, context):
        context[self.name] = getattr(settings, self.value.resolve(context, True), "")
        return ''

@register.tag('get_settings_value')
def do_assign(parser, token):
    bits = token.split_contents()
    if len(bits) != 3:
        raise template.TemplateSyntaxError("'%s' tag takes two arguments" % bits[0])
    value = parser.compile_filter(bits[2])
    return AssignNode(bits[1], value)

Ваш шаблон:

{% load my_custom_tags %}

# Set local template variable:
{% get_settings_value settings_debug "DEBUG" %}

# Output settings_debug variable:
{{ settings_debug }}

# Use variable in if statement:
{% if settings_debug %}
... do something ...
{% else %}
... do other stuff ...
{% endif %}

Смотрите документацию Django о том, как создавать собственные теги шаблонов здесь: https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/

NullIsNot0
источник
1
{% if settings_debug %}
user66081
Спасибо @ user66081! Изменено {% if settings_debug == True %}на предложенное{% if settings_debug %}
NullIsNot0
7

Если используется представление на основе классов:

#
# in settings.py
#
YOUR_CUSTOM_SETTING = 'some value'

#
# in views.py
#
from django.conf import settings #for getting settings vars

class YourView(DetailView): #assuming DetailView; whatever though

    # ...

    def get_context_data(self, **kwargs):

        context = super(YourView, self).get_context_data(**kwargs)
        context['YOUR_CUSTOM_SETTING'] = settings.YOUR_CUSTOM_SETTING

        return context

#
# in your_template.html, reference the setting like any other context variable
#
{{ YOUR_CUSTOM_SETTING }}
Билл Паецке
источник
3

Я обнаружил, что это самый простой подход для Django 1.3:

  1. views.py

    from local_settings import BASE_URL
    
    def root(request):
        return render_to_response('hero.html', {'BASE_URL': BASE_URL})
  2. hero.html

    var BASE_URL = '{{ JS_BASE_URL }}';
Майкл
источник
1

И IanSR, и bchhun предложили переопределить TEMPLATE_CONTEXT_PROCESSORS в настройках. Имейте в виду, что этот параметр имеет значение по умолчанию, которое может вызвать некоторые неприятные вещи, если вы переопределите его, не переустанавливая значения по умолчанию. Значения по умолчанию также изменились в последних версиях Django.

https://docs.djangoproject.com/en/1.3/ref/settings/#template-context-processors

TEMPLATE_CONTEXT_PROCESSORS по умолчанию:

TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages")
MrOodles
источник
1

Если бы мы сравнили теги контекста и шаблона на одной переменной, то знание более эффективного варианта могло бы принести пользу. Тем не менее, вам может быть лучше окунуться в настройки только из шаблонов, которые нуждаются в этой переменной. В этом случае нет смысла передавать переменную во все шаблоны. Но если вы отправляете переменную в общий шаблон, такой как шаблон base.html, то это не имеет значения, поскольку шаблон base.html отображается при каждом запросе, поэтому вы можете использовать любой из этих методов.

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

Пример: get_from_settings my_variable as my_context_value

Пример: get_from_settings my_variable my_default as my_context_value

class SettingsAttrNode(Node):
    def __init__(self, variable, default, as_value):
        self.variable = getattr(settings, variable, default)
        self.cxtname = as_value

    def render(self, context):
        context[self.cxtname] = self.variable
        return ''


def get_from_setting(parser, token):
    as_value = variable = default = ''
    bits = token.contents.split()
    if len(bits) == 4 and bits[2] == 'as':
        variable = bits[1]
        as_value = bits[3]
    elif len(bits) == 5 and bits[3] == 'as':
        variable     = bits[1]
        default  = bits[2]
        as_value = bits[4]
    else:
        raise TemplateSyntaxError, "usage: get_from_settings variable default as value " \
                "OR: get_from_settings variable as value"

    return SettingsAttrNode(variable=variable, default=default, as_value=as_value)

get_from_setting = register.tag(get_from_setting)
un33k
источник
Или вы можете использовать SITE_EXTRA_CONTEXT_DICTв Finalware, чтобы сделать это для вас.
un33k