¿Puedo acceder a las constantes en settings.py desde plantillas en Django?

367

Tengo algunas cosas en settings.py a las que me gustaría poder acceder desde una plantilla, pero no puedo entender cómo hacerlo. Ya lo intenté

{{CONSTANT_NAME}}

Pero eso no parece funcionar. es posible?

Paul Wicks
fuente
Si está buscando cómo pasar una configuración a cada respuesta, mire la respuesta de bchunn sobre procesadores de contexto
Zags
1
La respuesta de @jkbrzt es una solución preempaquetada que resuelve este problema rápida y fácilmente. Los futuros lectores deben echar un vistazo a esta stackoverflow.com/a/25841039/396005 sobre la respuesta aceptada
Bron Davies

Respuestas:

183

Django proporciona acceso a ciertas constantes de configuración de uso frecuente de la plantilla, como settings.MEDIA_URLalgunas de las configuraciones de idioma, si utiliza las vistas genéricas integradas de django o pasa un argumento de palabra clave de instancia de contexto en la render_to_responsefunción de acceso directo. Aquí hay un ejemplo de cada caso:

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

Estas vistas tendrán varias configuraciones de uso frecuente como settings.MEDIA_URLdisponibles para la plantilla como{{ MEDIA_URL }} , etc.

Si está buscando acceso a otras constantes en la configuración, simplemente desempaquete las constantes que desee y agréguelas al diccionario de contexto que está utilizando en su función de visualización, de esta manera:

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)

Ahora puede acceder settings.FAVORITE_COLORa su plantilla como {{ favorite_color }}.

Prairiedogg
fuente
66
Vale la pena señalar que los valores específicos agregados mediante el uso de RequestContext dependen del valor de TEMPLATE_CONTEXT_PROCESSORS. Por lo tanto, si desea que se pasen valores adicionales en todas partes, simplemente escriba su propio procesador de contexto y agréguelo a TEMPLATE_CONTEXT_PROCESSORS.
Carl Meyer el
Un punto de coherencia, en las vistas genéricas, y muchas de las aplicaciones principales y contrib, el contexto adicional se llama extra_context, y muy a menudo se incluye en los argumentos de la vista.
Soviut 01 de
"Django proporciona acceso a ciertas constantes de configuración de uso frecuente de la plantilla, como settings.MEDIA_URL". Esto no parece funcionar en Django 1.3, aunque probablemente lo estoy usando mal. ¿Hay alguna documentación para esta función?
SystemParadox
1
@asofyan sí, agregue crear un procesador de contexto de plantilla personalizado y agregar a TEMPLATE_CONTEXT_PROCESSORS en settings.py.
Paolo
14
Mire django-settings-exportpara evitar la necesidad de escribir este código en cada vista.
qris
441

Si es un valor que le gustaría tener para cada solicitud y plantilla, es más apropiado usar un procesador de contexto .

Así es cómo:

  1. Crea un context_processors.pyarchivo en el directorio de tu aplicación. Digamos que quiero tener el ADMIN_PREFIX_VALUEvalor en cada contexto:

    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. agregue su procesador de contexto a su archivo settings.py :

    TEMPLATES = [{
        # whatever comes before
        'OPTIONS': {
            'context_processors': [
                # whatever comes before
                "your_app.context_processors.admin_media",
            ],
        }
    }]
  3. Use RequestContexten su vista para agregar sus procesadores de contexto en su plantilla. El renderatajo hace esto automáticamente:

    from django.shortcuts import render
    
    def my_view(request):
        return render(request, "index.html")
  4. y finalmente, en tu plantilla:

    ...
    <a href="{{ ADMIN_MEDIA_URL }}">path to admin media</a>
    ...
bchhun
fuente
32
@MarkEssel Estos aros se hacen para que la variable sea accesible en cada vista que realice siempre que use la función RequestContext. Siempre puede buscar una variable de configuración manualmente en cada vista. Elegiría un procesador de contexto reutilizable en cualquier momento en lugar de un buen viejo copiar y pegar.
bchhun
55
haciendo todo lo posible para evitar copiar / pegar en todas partes posible. ¿requerirían todas y cada una de las aplicaciones (dentro de un proyecto) un context_processor.py, ¿hay alguna manera de construir un context_processor para todas ellas?
Mark Essel
10
@bchhun que acabo de probar (Django 1.3): compartir un procesador de contexto entre aplicaciones funciona bien. :-) Puse context_process.pyjusto al lado de mi settings.pyarchivo y agregué "context_processors.admin_media"a mi TEMPLATE_CONTEXT_PROCESSORSlista. Además, es posible que desee agregar una nota en su respuesta sobre el hecho de que el valor predeterminado de TEMPLATE_CONTEXT_PROCESSORS no está vacío, por lo que si algún código existente usa alguno de los valores establecidos por esos procesadores de contexto predeterminados, no funcionarán a menos que los vuelva a agregar a la lista explícitamente.
MiniQuark
55
@ MarkEssel No es doloroso en absoluto, simplemente lo ha explicado todo. Realmente son solo 6 líneas cortas (pasos 1 y 2). Los pasos 3 y 4 o su equivalente son necesarios para la mayoría de las plantillas de todos modos.
Rick Westera
2
A partir de Django 1.3, puede usar el renderacceso directo para evitar tener que incluir explícitamente RequestContext: docs.djangoproject.com/en/1.6/topics/http/shortcuts/#render
yndolok
269

Encuentro que el enfoque más simple es una sola etiqueta de plantilla personalizada :

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, "")

Uso:

{% settings_value "LANGUAGE_CODE" %}
Berislav Lopac
fuente
17
Me encanta tener acceso a pedido a cualquier configuración en plantillas, y esto lo proporciona con elegancia. Esto es realmente mucho mejor que las otras respuestas si con frecuencia usará varias configuraciones en sus plantillas: 1) La respuesta aceptada es incompatible o torpe con vistas basadas en clases. 2) Con la solución de procesador de contexto de plantilla sobre votada, tendría que especificar configuraciones individuales (o todas) y se ejecutaría para cada solicitud que haga que una plantilla sea ineficiente. 3) Es más simple que la etiqueta más compleja de arriba.
Ben Roberts
16
@BenRoberts Estoy de acuerdo en que esta es una solución elegante ... pero solo para pequeños proyectos con un solo desarrollador que hace todo. Si tiene personas / equipos separados para el diseño y el desarrollo, entonces esta solución es probablemente la peor . ¿Qué impide que el diseñador abuse de esta etiqueta con algo como {% settings_value "DATABASES" %}:? Este caso de uso debería hacer obvio por qué la configuración no está disponible en las plantillas para empezar.
mkoistinen
23
"Todos estamos consintiendo adultos aquí"
viernes
11
Disculpe por ser un novato. ¿Dónde pones este código? Views.py? O en un nuevo archivo?
Noel Llevares
13
Para ser claro para otras personas, debe: 1) crear una templatetagscarpeta dentro de su aplicación con un __init__.pyarchivo vacío y este código como settings.pydentro de esa carpeta. 2) en su plantilla agrega {% load settings %}y luego usa su nueva etiqueta.
damio
95

Echa un vistazo django-settings-export(descargo de responsabilidad: soy el autor de este proyecto).

Por ejemplo...

$ 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>
Jakub Roztocil
fuente
1
Y tenga en cuenta que en sus puntos de vista debe usar rendery norender_to_response
Everett Toews
Tengo requisito similar para leer los valores de configuración de las plantillas, pero estoy recibiendo el error 500 cuando agrego 'django_settings_export.settings_export' en el establecimiento de file.Can que sugieren lo que estoy haciendo mal aquí
Piyush Sahu
3
Es 2019 y lo estoy usando en mi proyecto. ¡Gracias!
sivabudh
1
Estoy de acuerdo con @sivabudh. Esta es también la mejor solución para mí porque 1. Está centralizado, lo que significa que no necesito carpetas y archivos adicionales. 2. Puedo ver el espacio de nombres de configuración en mi plantilla, lo cual es muy útil para obtener las referencias de muchas aplicaciones.
ywiyogo
46

Otra forma de hacerlo es crear una etiqueta de plantilla personalizada que le permita extraer valores de la configuración.

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

Entonces puedes usar:

{% value_from_settings "FQDN" %}

para imprimirlo en cualquier página, sin saltar por los aros del procesador de contexto.

fadedbee
fuente
66
Creo que esta es la solución más elegante, ya que funciona como dropin sin cambiar el código.
ovejas voladoras
1
que puede dejar el resto de su aplicación sin alterar: agrega una etiqueta y la usa, en lugar de tener que agregar procesadores de contexto (lo que significa que tiene que editar su aplicación en varios lugares)
flying sheep
2
@ Mark - en produi / src / produi / template_utils / templatetags / custom_template_filters.py template_utils se hace referencia desde settings.py INSTALLED_APPS - también vea docs.djangoproject.com/en/dev/howto/custom-template-tags
fadedbee
agradezco la ayuda de Chris, agregó una aplicación mutil con un subdirectorio templatetags que incluye custom_template_filters. Todavía aparece un error en homepage.html "Etiqueta de bloque no válida: 'value_from_settings', esperado 'endblock' o 'endblock banner'"
Mark Essel
Creo que esto va en contra de "explícito es mejor que implícito", utilizando la versión de decorador de contexto, usted elige exactamente qué configuración exponer.
sjh
29

Me gusta la solución de Berislav, porque en sitios simples, es limpia y efectiva. Lo que NO me gusta es exponer todas las constantes de configuración willy-nilly. Entonces, lo que terminé haciendo fue esto:

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

Uso:

{% settings_value "CONSTANT_NAME_1" %}

Esto protege las constantes que no ha nombrado del uso en la plantilla, y si desea ser realmente elegante, puede establecer una tupla en la configuración y crear más de una etiqueta de plantilla para diferentes páginas, aplicaciones o áreas, y simplemente combine una tupla local con la tupla de configuración según sea necesario, luego comprenda la lista para ver si el valor es aceptable.
Estoy de acuerdo, en un sitio complejo, esto es un poco simplista, pero hay valores que sería bueno tener universalmente en las plantillas, y esto parece funcionar bien. ¡Gracias a Berislav por la idea original!

MontyThreeCard
fuente
55
¿Por qué no simplemente?if name in ALLOWABLE_VALUES: ...
Viernes
Porque pensé que estaba siendo inteligente y quería evitar que las subcadenas activaran la configuración var. ;-) El retorno probablemente debería ser: return getattr (settings, is_allowable, '')
MontyThreeCard
55
Solo para aclarar a cualquiera que se pregunte: 'val' in ('val_first', 'second_val',)es False, no hay problema de subcadena aquí.
viernes
2
¿Cómo puedo usar esto en la ifdeclaración? quiero verificar el DEBUGvalor
AJ
Si alguien necesitara una versión con re incluido gist.github.com/BrnoPCmaniak/632f56ddb907108b3d43fa862510dfca
Filip Dobrovolný
12

Mejoré un poco la respuesta de chrisdew (para crear tu propia etiqueta).

Primero, cree el archivo yourapp/templatetags/value_from_settings.pyen el que define su propia etiqueta nueva 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

Puede usar esta etiqueta en su plantilla a través de:

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

o vía

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

La ventaja de la as ...notación es que esto hace que sea fácil de usar en blocktransbloques a través de un simple {{my_fqdn}}.

pklaus
fuente
12

Agregar una respuesta con instrucciones completas para crear una etiqueta de plantilla personalizada que resuelva esto, con Django 2.0+

En su carpeta de aplicaciones, cree una carpeta llamada templatetags . En él, cree __init__.py y custom_tags.py :

Estructura de carpetas de etiquetas personalizadas

En custom_tags.py, cree una función de etiqueta personalizada que proporcione acceso a una clave arbitraria en la configuración constante:

from django import template
from django.conf import settings

register = template.Library()

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

Para entender este código, recomiendo leer la sección sobre etiquetas simples en los documentos de Django.

Luego, debe informar a Django sobre esta etiqueta personalizada (y cualquier otra adicional) cargando este archivo en cualquier plantilla donde la vaya a usar. Al igual que necesita cargar la etiqueta estática integrada:

{% load custom_tags %}

Una vez cargado, puede usarse como cualquier otra etiqueta, solo proporcione la configuración específica que necesita devolver. Entonces, si tiene una variable BUILD_VERSION en su configuración:

{% get_setting "BUILD_VERSION" %}

Esta solución no funcionará con matrices, pero si lo necesita, podría estar poniendo mucha lógica en sus plantillas.

Nota: Una solución más limpia y segura probablemente sería crear un procesador de contexto personalizado donde agregue la configuración que necesita a un contexto disponible para todas las plantillas. De esta forma, reduce el riesgo de generar configuraciones confidenciales en sus plantillas por error.

Andreas Bergström
fuente
9

Agregue este código a un archivo llamado context_processors.py:

from django.conf import settings as django_settings


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

Y luego, en su archivo de configuración, incluya una ruta como 'speedy.core.base.context_processors.settings'(con el nombre y la ruta de su aplicación) en la 'context_processors'configuración de TEMPLATES.

(Puede ver, por ejemplo, settings / base.py y context_processors.py ).

Luego puede usar la configuración específica en cualquier código de plantilla. Por ejemplo:

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

Actualización: el código anterior expone toda la configuración a plantillas, incluida información confidencial como la suya SECRET_KEY. Un hacker podría abusar de esta función para mostrar dicha información en las plantillas. Si desea exponer solo configuraciones específicas a las plantillas, use este código en su lugar:

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,
    }
Speedy Match
fuente
1
Ayer me encontré con este problema, encontré esta publicación, luego otras 2 y una publicación de blog y sentí que cada una de ellas era demasiado complicada (desafortunadamente no llegué tan lejos en la página, lástima). Entonces terminé rodando la mía, que es EXACTAMENTE esta solución. Acabo de regresar porque me molestaba que la gente recomendara complementos y un código de lotta completo cuando ^^^ la función de 3 líneas y el cambio de 1 línea en settings.py.
DXM
@DXM ¡Gracias!
Speedy Match
En realidad, mi solución expone toda la configuración a plantillas, incluida información confidencial como SECRET_KEY. Un pirata informático puede abusar de esta función para mostrar dicha información en las plantillas.
Speedy Match
Actualicé mi respuesta.
Speedy Match
bueno ... genial, ahora mi sitio web tiene el mismo problema :) Pero ... podría estar perdiendo algo, sin embargo, ¿estamos seguros de que hay un problema? Las plantillas son esencialmente las mismas que el código fuente de su sitio web, ¿no es así? Se almacenan del lado del servidor y son inaccesibles directamente desde el front-end. Si un hacker puede cambiar una plantilla, en ese momento podría cambiar cualquier archivo .py.
DXM
8

El ejemplo anterior de bchhun es bueno, excepto que necesita construir explícitamente su diccionario de contexto desde settings.py. A continuación se muestra un ejemplo NO PROBADO de cómo podría crear automáticamente el diccionario de contexto a partir de todos los atributos en mayúsculas de settings.py (re: "^ [A-Z0-9 _] + $").

Al final de 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
fuente
8

Si alguien encuentra esta pregunta como yo, entonces publicaré mi solución que funciona en Django 2.0:

Esta etiqueta asigna algunos valores de variable settings.py a la variable de la plantilla:

Uso: {% get_settings_value template_var "SETTINGS_VAR" %}

app / 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)

Su plantilla:

{% 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 %}

Consulte la documentación de Django sobre cómo crear etiquetas de plantilla personalizadas aquí: https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/

NullIsNot0
fuente
1
{% if settings_debug %}
user66081
Gracias @ user66081! Cambiado {% if settings_debug == True %}a su sugerencia{% if settings_debug %}
NullIsNot0
7

Si usa una vista basada en clases:

#
# 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 }}
Bill Paetzke
fuente
3

Encontré que este es el enfoque más simple para 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 }}';
Miguel
fuente
1

Tanto IanSR como bchhun sugirieron anular TEMPLATE_CONTEXT_PROCESSORS en la configuración. Tenga en cuenta que esta configuración tiene un valor predeterminado que puede causar algunos problemas si lo anula sin restablecer los valores predeterminados. Los valores predeterminados también han cambiado en versiones recientes de Django.

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

El TEMPLATE_CONTEXT_PROCESSORS predeterminado:

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
fuente
1

Si tuviéramos que comparar etiquetas de contexto frente a plantillas en una sola variable, entonces conocer la opción más eficiente podría ser beneficioso. Sin embargo, es mejor que se sumerja en la configuración solo de las plantillas que necesitan esa variable. En ese caso, no tiene sentido pasar la variable a todas las plantillas. Pero si está enviando la variable a una plantilla común como la plantilla base.html, entonces no importaría ya que la plantilla base.html se representa en cada solicitud, por lo que puede usar cualquiera de los métodos.

Si decide utilizar la opción de etiquetas de plantilla, utilice el siguiente código, ya que le permite pasar un valor predeterminado valor , en caso de que la variable en cuestión no definida.

Ejemplo: get_from_settings my_variable como my_context_value

Ejemplo: get_from_settings my_variable my_default como 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
fuente
O puede utilizar SITE_EXTRA_CONTEXT_DICTen finalware que lo haga por usted.
un33k