Posso accedere alle costanti in settings.py dai modelli in Django?


367

Ho alcune cose in settings.py a cui vorrei poter accedere da un modello, ma non riesco a capire come farlo. Ci ho già provato

{{CONSTANT_NAME}}

ma questo non sembra funzionare. È possibile?


Se stai cercando come passare un'impostazione a ogni risposta, guarda la risposta di bchunn sui processori di contesto
Zags

1
La risposta di @jkbrzt è una soluzione preconfezionata che risolve questo problema rapidamente e facilmente. I futuri lettori dovrebbero dare un'occhiata a questo stackoverflow.com/a/25841039/396005 sulla risposta accettata
Bron Davies,

Risposte:


183

Django fornisce l'accesso a determinate costanti di impostazioni utilizzate di frequente al modello come settings.MEDIA_URLe ad alcune delle impostazioni della lingua se si utilizzano le viste generiche incorporate di django o si passa a un argomento della parola chiave dell'istanza di contesto nella render_to_responsefunzione di scelta rapida. Ecco un esempio di ciascun 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))

Queste viste avranno entrambe diverse impostazioni usate frequentemente come settings.MEDIA_URLdisponibili per il modello come{{ MEDIA_URL }} , ecc.

Se stai cercando l'accesso ad altre costanti nelle impostazioni, decomprimi semplicemente le costanti che desideri e aggiungile al dizionario di contesto che stai utilizzando nella tua funzione di visualizzazione, in questo modo:

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)

Ora puoi accedere settings.FAVORITE_COLORal tuo modello come {{ favorite_color }}.


66
Vale la pena notare che i valori specifici aggiunti utilizzando un RequestContext dipendono dal valore di TEMPLATE_CONTEXT_PROCESSORS. Pertanto, se si desidera che vengano trasferiti valori aggiuntivi ovunque, è sufficiente scrivere il proprio processore di contesto e aggiungerlo a TEMPLATE_CONTEXT_PROCESSORS.
Carl Meyer,

Un punto sulla coerenza, nelle viste generiche e in molte delle app core e contrib, il contesto aggiuntivo è chiamato extra_context e molto spesso è incluso negli argomenti della vista.
Soviet

"Django fornisce l'accesso a determinate costanti di impostazioni utilizzate di frequente al modello come settings.MEDIA_URL". Questo non sembra funzionare in Django 1.3, anche se probabilmente lo sto usando male. C'è della documentazione per questa funzione?
SystemParadox,

1
@asofyan sì, aggiungi crea un processore di contesto modello personalizzato e aggiungi a TEMPLATE_CONTEXT_PROCESSORS in settings.py.
Paolo,

14
Cerca django-settings-exportdi evitare la necessità di scrivere questo codice in ogni vista.
Qris

441

Se è un valore che desideri avere per ogni richiesta e modello, utilizzando un processore di contesto è più appropriato .

Ecco come:

  1. Crea un context_processors.pyfile nella directory dell'app. Diciamo che voglio avere il ADMIN_PREFIX_VALUEvalore in ogni contesto:

    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. aggiungi il tuo processore di contesto al tuo file settings.py :

    TEMPLATES = [{
        # whatever comes before
        'OPTIONS': {
            'context_processors': [
                # whatever comes before
                "your_app.context_processors.admin_media",
            ],
        }
    }]
  3. Utilizzare RequestContextnella vista per aggiungere i processori di contesto nel modello. Il rendercollegamento fa automaticamente questo:

    from django.shortcuts import render
    
    def my_view(request):
        return render(request, "index.html")
  4. e infine, nel tuo modello:

    ...
    <a href="{{ ADMIN_MEDIA_URL }}">path to admin media</a>
    ...

32
@MarkEssel Questi cerchi sono fatti in modo che la variabile sia accessibile in ogni vista che farai purché utilizzi la funzione RequestContext. Puoi sempre recuperare manualmente una variabile di impostazione in ogni vista. Sceglierei un processore di contesto riutilizzabile in qualsiasi momento anziché un buon vecchio copia e incolla.
bchhun,

5
facendo del mio meglio per evitare di copiare / incollare ovunque possibile. ogni singola app (all'interno di un progetto) richiederebbe un context_processor.py, c'è un modo per costruire un context_processor per tutti loro?
Mark Essel,

10
@bchhun Ho appena testato (Django 1.3): la condivisione di un processore di contesto tra app funziona perfettamente. :-) Ho messo context_process.pyproprio accanto al mio settings.pyfile e aggiunto "context_processors.admin_media"alla mia TEMPLATE_CONTEXT_PROCESSORSlista. Inoltre, potresti voler aggiungere una nota nella tua risposta sul fatto che il valore predefinito di TEMPLATE_CONTEXT_PROCESSORS non è vuoto, quindi se un codice esistente utilizza uno dei valori impostati da tali processori di contesto predefiniti, non funzioneranno se non li aggiungi di nuovo all'elenco esplicitamente.
MiniQuark

5
@MarkEssel Non è affatto doloroso - ha appena spiegato tutto. Sono in realtà solo 6 linee brevi (passaggi 1 e 2). I passaggi 3 e 4 o il loro equivalente sono richiesti per la maggior parte dei modelli comunque.
Rick Westera,

2
A partire da Django 1.3, è possibile utilizzare il rendercollegamento per evitare di includere esplicitamente RequestContext: docs.djangoproject.com/en/1.6/topics/http/shortcuts/#render
yndolok

269

Trovo che l'approccio più semplice sia un singolo tag modello personalizzato :

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

17
Adoro avere accesso su richiesta a qualsiasi impostazione nei modelli e questo lo fornisce elegantemente. Questo è davvero molto meglio delle altre risposte se utilizzerai frequentemente varie impostazioni nei tuoi modelli: 1) La risposta accettata è incompatibile o goffa con le viste basate sulla classe. 2) Con la soluzione di processore di contesto modello sovrastimata, dovresti specificare le singole impostazioni (o tutte) e verrebbe eseguito per ogni singola richiesta che rende un modello - inefficiente! 3) È più semplice del tag più complesso sopra.
Ben Roberts,

16
@BenRoberts Sono d'accordo sul fatto che questa sia una soluzione elegante ... ma solo per piccoli progetti con un singolo sviluppatore che fa tutto. Se hai persone / team separati per la progettazione e lo sviluppo, questa soluzione è probabilmente la peggiore . Cosa impedisce al designer di abusare di questo tag con qualcosa del tipo {% settings_value "DATABASES" %}:? Questo caso d'uso dovrebbe rendere ovvio il motivo per cui le impostazioni non sono disponibili nei modelli all'inizio.
mkoistinen,

23
"Siamo tutti adulti consenzienti qui"
dal

11
Scusami se sono un principiante. Dove metti questo codice? Views.py? O su un nuovo file?
Noel Llevares,

13
per essere chiari per gli altri, devi: 1) creare una templatetagscartella all'interno della tua app con un __init__.pyfile vuoto e questo codice come settings.pyall'interno di quella cartella. 2) nel tuo modello aggiungi {% load settings %}e poi usi il tuo nuovo tag!
damio,

95

Check out django-settings-export(disclaimer: sono l'autore di questo progetto).

Per esempio...

$ 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
E nota che nelle tue opinioni devi usare rendere nonrender_to_response
Everett Toews il

Ho un requisito simile per leggere i valori dall'impostazione nei modelli, ma ricevo 500 errori quando aggiungo 'django_settings_export.settings_export' nel file di impostazione. Puoi suggerire cosa sto facendo di sbagliato qui
Piyush Sahu

3
È il 2019 e lo sto usando nel mio progetto. Grazie!
sivabudh,

1
Sono d'accordo con @sivabudh. Questa è anche per me la soluzione migliore perché 1. È centralizzata, il che significa che non ho bisogno di cartelle e file extra, 2. Posso vedere lo spazio dei nomi delle impostazioni nel mio modello, che è molto utile per ottenere i riferimenti di molte applicazioni.
ywiyogo,

46

Un altro modo per farlo è quello di creare un tag modello personalizzato che ti permetta di pescare valori fuori dalle impostazioni.

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

È quindi possibile utilizzare:

{% value_from_settings "FQDN" %}

per stamparlo su qualsiasi pagina, senza saltare attraverso i cerchi del processore di contesto.


6
penso che questa sia la soluzione più elegante, in quanto funziona come dropin senza cambiare codice.
pecora volante

1
che puoi lasciare inalterato il resto della tua applicazione: aggiungi un tag e lo usi, invece di dover aggiungere processori di contesto (il che significa che devi modificare la tua applicazione in più punti)
flying sheep

2
@Mark - in produi / src / produi / template_utils / templatetags / custom_template_filters.py template_utils è referenziato da settings.py INSTALLED_APPS
fadedbee

apprezzo l'aiuto chris, ha aggiunto un'app mutil con una sottodirectory templatetags che include custom_template_filters. Viene ancora visualizzato un errore in homepage.html "Tag blocco non valido: 'value_from_settings', 'endblock' previsto o 'banner endblock'"
Mark Essel,

Penso che ciò vada contro "esplicito è meglio che implicito", usando la versione del decoratore di contesto scegli esattamente quali impostazioni esporre.
sabato

29

Mi piace la soluzione di Berislav, perché su siti semplici, è pulita ed efficace. Quello che NON mi piace è esporre tutte le costanti delle impostazioni, volenti o nolenti. Quindi quello che ho finito per fare è stato questo:

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

Questo protegge qualsiasi costante che non hai nominato dall'uso nel modello e, se vuoi essere davvero fantasioso, potresti impostare una tupla nelle impostazioni e creare più di un tag modello per pagine, app o aree diverse e semplicemente combinare una tupla locale con la tupla delle impostazioni in base alle esigenze, quindi fare la comprensione dell'elenco per vedere se il valore è accettabile.
Sono d'accordo, su un sito complesso, questo è un po 'semplicistico, ma ci sono valori che sarebbe bello avere universalmente nei template, e questo sembra funzionare bene. Grazie a Berislav per l'idea originale!


5
perché non semplicementeif name in ALLOWABLE_VALUES: ...
dal

Perché pensavo di essere intelligente e volevo impedire alle sottostringhe di innescare le impostazioni var. ;-) Il ritorno dovrebbe probabilmente essere: return getattr (settings, is_allowable, '')
MontyThreeCard

5
Giusto per chiarire per chiunque si 'val' in ('val_first', 'second_val',)stia chiedendo: non c'è Falsenessun problema di sottostringa qui.
dal

2
Come posso usare questo nella ifdichiarazione? voglio controllare il DEBUGvalore
AJ,

Se qualcuno avesse bisogno della versione con re incluso gist.github.com/BrnoPCmaniak/632f56ddb907108b3d43fa862510dfca
Filip Dobrovolný

12

Ho migliorato un po ' la risposta di chrisdew (per creare il tuo tag).

Innanzitutto, crea il file yourapp/templatetags/value_from_settings.pyin cui definisci il tuo nuovo 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

Puoi utilizzare questo tag nel tuo modello tramite:

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

o via

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

Il vantaggio della as ...notazione è che questo lo rende facile da usare in blocktransblocchi tramite un semplice {{my_fqdn}}.


12

Aggiunta di una risposta con istruzioni complete per la creazione di un tag modello personalizzato che risolva questo problema con Django 2.0+

Nella cartella dell'app, crea una cartella denominata templatetags . In esso, crea __init__.py e custom_tags.py :

Struttura delle cartelle dei tag personalizzati

In custom_tags.py crea una funzione tag personalizzata che fornisce l'accesso a una chiave arbitraria nella costante delle impostazioni :

from django import template
from django.conf import settings

register = template.Library()

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

Per capire questo codice ti consiglio di leggere la sezione sui semplici tag nei documenti Django.

Quindi, devi rendere Django consapevole di questo (e di ogni altro tag) personalizzato caricando questo file in qualsiasi modello in cui lo utilizzerai. Proprio come è necessario caricare il tag statico incorporato:

{% load custom_tags %}

Una volta caricato, può essere utilizzato come qualsiasi altro tag, basta fornire l'impostazione specifica che è necessario restituire. Quindi se hai una variabile BUILD_VERSION nelle tue impostazioni:

{% get_setting "BUILD_VERSION" %}

Questa soluzione non funzionerà con le matrici, ma se è necessario è possibile che si stia mettendo molta logica nei modelli.

Nota: una soluzione più pulita e in sicurezza sarebbe probabilmente quella di rendere un processore di contesto personalizzato in cui aggiungere le impostazioni necessarie a un contesto disponibile per tutti i modelli. In questo modo si riduce il rischio di emettere per errore impostazioni sensibili nei modelli.


9

Aggiungi questo codice a un file chiamato context_processors.py:

from django.conf import settings as django_settings


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

Quindi, nel file delle impostazioni, includi un percorso come 'speedy.core.base.context_processors.settings'(con il nome e il percorso dell'app) nelle 'context_processors'impostazioni in TEMPLATES.

(Puoi vedere ad esempio settings / base.py e context_processors.py ).

Quindi è possibile utilizzare l'impostazione specifica in qualsiasi codice modello. Per esempio:

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

Aggiornamento: il codice sopra espone tutte le impostazioni ai modelli, comprese le informazioni sensibili come la tua SECRET_KEY. Un hacker potrebbe abusare di questa funzione per visualizzare tali informazioni nei modelli. Se si desidera esporre solo le impostazioni specifiche ai modelli, utilizzare invece questo codice:

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
Ho riscontrato questo problema ieri, ho trovato questo post, poi altri 2 e un post sul blog e ho sentito che ognuno di loro era eccessivamente complicato (sfortunatamente non ce l'ho fatta così in fondo alla pagina, peccato per me). Quindi ho finito per lanciare il mio, che è ESATTAMENTE questa soluzione. Sono appena tornato perché mi dava fastidio che le persone stessero raccomandando plug-in e un intero codice di lotta quando questa funzione ^^^ a 3 righe e 1 riga in settings.py.
DXM,

@DXM Grazie!
Partita veloce del

In realtà la mia soluzione espone tutte le impostazioni ai modelli, comprese le informazioni sensibili come SECRET_KEY. Un hacker potrebbe abusare di questa funzione per visualizzare tali informazioni nei modelli.
Partita veloce del

Ho aggiornato la mia risposta.
Partita veloce del

bene ... fantastico, ora il mio sito web ha lo stesso problema :) Ma ... Potrei mancare qualcosa, comunque, siamo sicuri che ci sia un problema? I modelli sono essenzialmente gli stessi del codice sorgente per il tuo sito Web, vero? Sono memorizzati sul lato server e inaccessibili direttamente dal front-end. Se un hacker può cambiare un modello, a quel punto potrebbe cambiare qualsiasi file .py.
DXM,

8

L'esempio sopra riportato da bchhun è utile, tranne per il fatto che è necessario creare esplicitamente il dizionario di contesto da settings.py. Di seguito è riportato un esempio UNTESTED di come è possibile creare automaticamente il dizionario di contesto da tutti gli attributi maiuscoli di settings.py (re: "^ [A-Z0-9 _] + $").

Alla fine di 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',
...
)

8

Se qualcuno trova questa domanda come ho fatto io, posterò la mia soluzione che funziona su Django 2.0:

Questo tag assegna alcuni valori della variabile settings.py alla variabile del modello:

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)

Il tuo modello:

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

Consulta la documentazione di Django su come creare tag modello personalizzati qui: https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/


1
{% if settings_debug %}
user66081,

Grazie @ user66081! Modificato in base {% if settings_debug == True %}al tuo suggerito{% if settings_debug %}
NullIsNot0

7

Se si utilizza una vista basata su classi:

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

Ho scoperto che questo è l'approccio più semplice per 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

Sia IanSR che bchhun hanno suggerito di sostituire TEMPLATE_CONTEXT_PROCESSORS nelle impostazioni. Tenere presente che questa impostazione ha un valore predefinito che può causare alcune cose errate se si esegue l'override senza ripristinare le impostazioni predefinite. Le impostazioni predefinite sono cambiate anche nelle ultime versioni di Django.

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

Il TEMPLATE_CONTEXT_PROCESSORS predefinito:

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

1

Se dovessimo confrontare i tag di contesto e di modello su una singola variabile, allora conoscere l'opzione più efficiente potrebbe essere positivo. Tuttavia, potrebbe essere meglio immergersi nelle impostazioni solo dai modelli che richiedono quella variabile. In tal caso, non ha senso passare la variabile in tutti i modelli. Ma se stai inviando la variabile in un modello comune come il modello base.html, non importa se il modello base.html viene visualizzato su ogni richiesta, quindi puoi utilizzare entrambi i metodi.

Se decidi di utilizzare l'opzione tag modello, utilizza il seguente codice in quanto ti consente di passare un valore predefinito , nel caso in cui la variabile in questione non fosse definita.

Esempio: get_from_settings my_variable come my_context_value

Esempio: get_from_settings my_variable my_default come 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)

Oppure puoi usare SITE_EXTRA_CONTEXT_DICTnel finalware per farlo per te.
Un33k
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.