Posso acessar constantes em settings.py a partir de modelos no Django?

367

Eu tenho algumas coisas em settings.py que gostaria de acessar de um modelo, mas não consigo descobrir como fazê-lo. Eu já tentei

{{CONSTANT_NAME}}

mas isso não parece funcionar. Isso é possível?

Paul Wicks
fonte
Se você está procurando como passar uma definição para cada resposta, olhar para a resposta de bchunn sobre processadores de contexto
Zags
11
A resposta do @jkbrzt é uma solução pré-empacotada que resolve esse problema com rapidez e facilidade. Futuros leitores deve dar uma olhada neste stackoverflow.com/a/25841039/396005 sobre a resposta aceita
Bron Davies

Respostas:

183

O Django fornece acesso a certas constantes de configurações usadas com freqüência ao modelo como settings.MEDIA_URLe algumas das configurações de idioma se você usar visualizações genéricas incorporadas pelo django ou passar um argumento de palavra-chave da instância de contexto na render_to_responsefunção de atalho. Aqui está um exemplo 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))

Essas visualizações terão várias configurações usadas com frequência, como settings.MEDIA_URLdisponíveis para o modelo como {{ MEDIA_URL }}etc.

Se você estiver procurando acesso a outras constantes nas configurações, basta descompactar as constantes desejadas e adicioná-las ao dicionário de contexto que você está usando na função de visualização, da seguinte forma:

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)

Agora você pode acessar settings.FAVORITE_COLORno seu modelo como {{ favorite_color }}.

prairiedogg
fonte
66
Vale ressaltar que os valores específicos adicionados usando um RequestContext dependem do valor de TEMPLATE_CONTEXT_PROCESSORS. Portanto, se você quiser que valores adicionais sejam passados ​​para qualquer lugar, basta escrever seu próprio processador de contexto e adicioná-lo a TEMPLATE_CONTEXT_PROCESSORS.
266 Carl
Um ponto sobre consistência, nas visualizações genéricas e em muitos dos aplicativos principais e de contribuição, o contexto adicional é chamado extra_context e, muitas vezes, é incluído nos argumentos da exibição.
Soviut 01/06/2009
"O Django fornece acesso a certas constantes de configurações usadas com freqüência ao modelo, como settings.MEDIA_URL". Isso não parece funcionar no Django 1.3, embora eu provavelmente esteja usando errado. Existe alguma documentação para esse recurso?
SystemParadox 28/10
11
@asofyan sim, adicione criar um processador de contexto de modelo personalizado e adicione a TEMPLATE_CONTEXT_PROCESSORS em settings.py.
Paolo
14
Observe django-settings-exportpara evitar a necessidade de escrever esse código em todas as visualizações.
QRIS
441

Se é um valor que você gostaria de ter para cada solicitação e modelo, usar um processador de contexto é mais apropriado.

Aqui está como:

  1. Crie um context_processors.pyarquivo no diretório do seu aplicativo. Digamos que eu quero ter o ADMIN_PREFIX_VALUEvalor em todos os contextos:

    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. adicione seu processador de contexto ao seu arquivo settings.py :

    TEMPLATES = [{
        # whatever comes before
        'OPTIONS': {
            'context_processors': [
                # whatever comes before
                "your_app.context_processors.admin_media",
            ],
        }
    }]
  3. Use RequestContextna sua exibição para adicionar seus processadores de contexto ao seu modelo. O renderatalho faz isso automaticamente:

    from django.shortcuts import render
    
    def my_view(request):
        return render(request, "index.html")
  4. e, finalmente, no seu modelo:

    ...
    <a href="{{ ADMIN_MEDIA_URL }}">path to admin media</a>
    ...
bchhun
fonte
32
@MarkEssel Esses hoops são feitos para que a variável fique acessível em todas as telas que você criar, desde que use a função RequestContext. Você sempre pode buscar uma variável de configurações manualmente em todas as telas. Eu escolheria um Processador de Contexto reutilizável a qualquer momento, em vez de copiar e colar.
bchhun
5
fazendo o meu melhor para evitar copiar / colar em todos os lugares possíveis. cada aplicativo (dentro de um projeto) exigiria um context_processor.py, existe uma maneira de construir um context_processor para todos eles?
precisa saber é o seguinte
10
@ bchhun Acabei de testar (Django 1.3): compartilhar um processador de contexto entre aplicativos funciona muito bem. :-) Coloquei context_process.pyao lado do meu settings.pyarquivo e adicionei "context_processors.admin_media"à minha TEMPLATE_CONTEXT_PROCESSORSlista. Além disso, convém adicionar uma observação em sua resposta sobre o fato de que o valor padrão de TEMPLATE_CONTEXT_PROCESSORS não está vazio; portanto, se algum código existente usar algum dos valores definidos por esses processadores de contexto padrão, eles não funcionarão, a menos que você os adicione novamente para a lista explicitamente.
MiniQuark 05/03
5
@ MarkEssel Não é nada doloroso - ele apenas explicou tudo. Na verdade, são apenas 6 linhas curtas (etapas 1 e 2). As etapas 3 e 4 ou equivalente são necessárias para a maioria dos modelos.
Rick Westera
2
A partir de Django 1.3, você pode usar o renderatalho para evitar ter de incluir explicitamente RequestContext: docs.djangoproject.com/en/1.6/topics/http/shortcuts/#render
yndolok
269

Acho que a abordagem mais simples é uma única tag de modelo personalizado :

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
fonte
17
Adoro ter acesso sob demanda a qualquer configuração em modelos, e isso fornece isso de maneira elegante. Isso é realmente muito melhor do que as outras respostas, se você frequentemente usar várias configurações em seus modelos: 1) A resposta aceita é incompatível ou desajeitada com as visualizações baseadas em classe. 2) Com a solução de processador de contexto de modelo super-votada, você teria que especificar configurações individuais (ou todas) e executaria todas as solicitações que renderizam um modelo - ineficiente! 3) É mais simples que a tag mais complexa acima.
21712 Ben Roberts
16
@BenRoberts Concordo que esta é uma solução elegante ... mas apenas para pequenos projetos com um único desenvolvedor que faz tudo. Se você tem pessoas / equipes separadas para design e desenvolvimento, essa solução é provavelmente a pior . O que impede o designer de abusar dessa tag com algo como {% settings_value "DATABASES" %}:? Esse caso de uso deve deixar óbvio por que as configurações não estão disponíveis nos modelos para começar.
Mkoistinen
23
"Todos nós somos adultos concordantes aqui"
frnhr 22/03/14
11
Perdoe-me por ser um novato. Onde você coloca esse código? Views.py? Ou em um novo arquivo?
61114 Noel Llevares
13
para ficar claro para outras pessoas, você precisa: 1) criar uma templatetagspasta dentro do seu aplicativo com um __init__.pyarquivo vazio e esse código settings.pydentro dessa pasta. 2) no seu modelo você adiciona {% load settings %}e depois usa sua nova tag!
damio 25/10/16
95

Confira django-settings-export(isenção de responsabilidade: sou o autor deste projeto).

Por exemplo...

$ 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
fonte
11
E nota que, em seus pontos de vista que você precisa para uso rendere nãorender_to_response
Everett Toews
Tenho exigência semelhante para ler os valores de ajuste nos modelos, mas eu estou recebendo erro 500 quando eu adiciono 'django_settings_export.settings_export' na definição file.Can você sugerir o que estou fazendo de errado aqui
Piyush Sahu
3
É 2019 e estou usando no meu projeto. Obrigado!
sivabudh
11
Eu concordo com @sivabudh. Essa é para mim também a melhor solução, porque 1. É centralizada, o que significa que não preciso de pastas e arquivos extras; 2. Posso ver o espaço para nome das configurações no meu modelo, o que é muito útil para obter as referências de muitos aplicativos.
Ywiyogo 25/04/19
46

Outra maneira de fazer isso é criar uma tag de modelo personalizada que pode permitir que você pesque valores fora das configurações.

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

Você pode então usar:

{% value_from_settings "FQDN" %}

imprimi-lo em qualquer página, sem pular os bastidores do processador de contexto.

fadedbee
fonte
6
Eu acho que esta é a solução mais elegante, pois funciona como dropin sem alterar o código.
ovelha voadora
11
que você pode deixar o restante do aplicativo inalterado: adicione uma tag e use-a, em vez de adicionar processadores de contexto (o que significa que você deve editar seu aplicativo em vários locais)
flying sheep sheep
2
@Mark - em produi / src / produi / template_utils / templatetags / custom_template_filters.py template_utils é referenciado em settings.py INSTALLED_APPS - também consulte docs.djangoproject.com/en/dev/howto/custom-template-tags
fadedbee
aprecie a ajuda chris, adicionou um aplicativo mutil com um subdiretório de tags de modelo, incluindo custom_template_filters. Ainda está recebendo um erro no homepage.html "Tag de bloco inválido: 'value_from_settings', esperado 'endblock' ou 'endblock banner'"
Mark Essel
Eu acho que isso vai contra "explícito é melhor do que implícito", usando a versão do decorador de contexto, você escolhe exatamente quais configurações expor.
sjh
29

Gosto da solução de Berislav, porque em sites simples ela é limpa e eficaz. O que eu NÃO gosto é de expor todas as constantes de configurações, quer ou não. Então o que acabei fazendo foi o seguinte:

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

Isso protege todas as constantes que você não nomeou para uso no modelo e, se quiser ser realmente sofisticado, pode definir uma tupla nas configurações e criar mais de uma marca de modelo para diferentes páginas, aplicativos ou áreas, e simplesmente combine uma tupla local com a tupla de configurações, conforme necessário, e faça a compreensão da lista para ver se o valor é aceitável.
Concordo que, em um site complexo, isso é um pouco simplista, mas há valores que seria bom ter universalmente em modelos, e isso parece funcionar bem. Obrigado a Berislav pela ideia original!

MontyThreeCard
fonte
5
porque não bastaif name in ALLOWABLE_VALUES: ...
frnhr
Porque eu pensei que estava sendo inteligente e queria impedir que as sub-strings acionassem as configurações var. ;-) O retorno provavelmente deve ser: return getattr (settings, is_allowable, '')
MontyThreeCard
5
Só para esclarecer para quem está se perguntando: 'val' in ('val_first', 'second_val',)é False, não substring problema aqui.
frnhr
2
Como posso usar isso na ifdeclaração? Eu quero verificar o DEBUGvalor
AJ
Se alguém precisar de versão com re incluído gist.github.com/BrnoPCmaniak/632f56ddb907108b3d43fa862510dfca
Filip Dobrovolný
12

Eu melhorei a resposta de chrisdew (para criar a sua própria etiqueta) um pouco.

Primeiro, crie o arquivo yourapp/templatetags/value_from_settings.pyno qual você define sua própria nova tag 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

Você pode usar essa tag no seu modelo por:

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

ou via

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

A vantagem da as ...notação é que isso facilita o uso em blocktransblocos por meio de um simples {{my_fqdn}}.

pklaus
fonte
12

Adicionando uma resposta com instruções completas para criar uma tag de modelo personalizada que resolve isso, com o Django 2.0+

Na sua pasta de aplicativos, crie uma pasta chamada tags de modelo . Nele, crie __init__.py e custom_tags.py :

Estrutura de pastas de tags personalizadas

No custom_tags.py, crie uma função de tag personalizada que forneça acesso a uma chave arbitrária na constante de configurações :

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 esse código, recomendo a leitura da seção sobre tags simples nos documentos do Django.

Então, você precisa tornar o Django ciente dessa tag personalizada (e de qualquer outra adicional) carregando esse arquivo em qualquer modelo em que você o usará. Assim como você precisa carregar a tag estática incorporada:

{% load custom_tags %}

Com ele carregado, ele pode ser usado como qualquer outra tag, basta fornecer a configuração específica que você precisa retornar. Portanto, se você tiver uma variável BUILD_VERSION em suas configurações:

{% get_setting "BUILD_VERSION" %}

Essa solução não funcionará com matrizes, mas se você precisar, poderá colocar muita lógica em seus modelos.

Nota: Uma solução mais limpa e à prova de falhas provavelmente seria criar um processador de contexto personalizado onde você adiciona as configurações necessárias a um contexto disponível para todos os modelos. Dessa forma, você reduz o risco de gerar configurações confidenciais em seus modelos por engano.

Andreas Bergström
fonte
9

Adicione este código a um arquivo chamado context_processors.py:

from django.conf import settings as django_settings


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

E, em seu arquivo de configurações, inclua um caminho como 'speedy.core.base.context_processors.settings'(com o nome e o caminho do seu aplicativo) no diretório'context_processors' configurações em TEMPLATES.

(Você pode ver, por exemplo, settings / base.py e context_processors.py ).

Em seguida, você pode usar a configuração específica em qualquer código de modelo. Por exemplo:

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

Atualização: o código acima expõe todas as configurações aos modelos, incluindo informações confidenciais como a sua SECRET_KEY. Um hacker pode abusar desse recurso para exibir essas informações nos modelos. Se você deseja expor apenas configurações específicas aos modelos, use este código:

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
fonte
11
Ontem, encontrei este problema, encontrei este post, depois outros 2 e um blog e senti que cada um deles era muito complicado (infelizmente não cheguei tão longe na página, que vergonha). Então acabei lançando o meu próprio, que é EXATAMENTE esta solução. Acabei de voltar porque estava me incomodando que as pessoas estivessem recomendando plugins e todo um código de lote quando essa função de 3 linhas e 1 linha mudam em settings.py.
DXM
@DXM Obrigado!
Speedy Match
Na verdade, minha solução expõe todas as configurações aos modelos, incluindo informações confidenciais como SECRET_KEY. Um hacker pode abusar desse recurso para exibir essas informações nos modelos.
Speedy Match
Eu atualizei minha resposta.
Speedy Match
bem ... ótimo, agora meu site tem o mesmo problema :) Mas ... posso estar faltando alguma coisa, no entanto, temos certeza de que há um problema? Os modelos são essencialmente os mesmos do código-fonte do seu site, não são? Eles são armazenados no servidor e inacessíveis diretamente do front-end. Se um hacker puder alterar um modelo, nesse ponto poderá alterar qualquer arquivo .py.
DXM
8

O exemplo acima de bchhun é bom, exceto que você precisa criar explicitamente seu dicionário de contexto a partir de settings.py. Abaixo está um exemplo UNTESTED de como você pode criar automaticamente o dicionário de contexto a partir de todos os atributos em maiúsculas de settings.py (re: "^ [A-Z0-9 _] + $").

No 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
fonte
8

Se alguém encontrar essa pergunta como eu, postarei minha solução que funciona no Django 2.0:

Essa tag atribui algum valor da variável settings.py à variável do modelo:

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)

Seu modelo:

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

Veja a documentação do Django como criar tags de modelo personalizadas aqui: https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/

NullIsNot0
fonte
11
{% if settings_debug %}
user66081
Obrigado @ user66081! Alterado {% if settings_debug == True %}para o sugerido{% if settings_debug %}
NullIsNot0
7

Se estiver usando uma exibição baseada em classe:

#
# 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
fonte
3

Eu achei que essa era a abordagem mais simples para o 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 }}';
Michael
fonte
1

IanSR e bchhun sugeriram substituir TEMPLATE_CONTEXT_PROCESSORS nas configurações. Esteja ciente de que essa configuração tem um padrão que pode causar algumas coisas estranhas se você a substituir sem redefinir os padrões. Os padrões também foram alterados nas versões recentes do Django.

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

O padrão 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
fonte
1

Se comparássemos as tags de contexto versus modelo em uma única variável, conhecer a opção mais eficiente poderia ser benéfico. No entanto, é melhor mergulhar nas configurações apenas de modelos que precisam dessa variável. Nesse caso, não faz sentido passar a variável para todos os modelos. Mas se você estiver enviando a variável para um modelo comum, como o modelo base.html, isso não importaria, pois o modelo base.html é renderizado em todas as solicitações, para que você possa usar os dois métodos.

Se você optar por usar a opção de tags de modelo, use o código a seguir, pois ele permite passar um valor padrão , caso a variável em questão não esteja definida.

Exemplo: get_from_settings my_variable como my_context_value

Exemplo: 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
fonte
Ou você pode usar SITE_EXTRA_CONTEXT_DICTem finalware para fazer isso por você.
Un33k