Come posso ottenere il nome di dominio del mio sito all'interno di un modello Django?


Risposte:


67

Penso che ciò che vuoi sia avere accesso al contesto della richiesta, vedi RequestContext.


140
request.META['HTTP_HOST']ti dà il dominio. In un modello sarebbe {{ request.META.HTTP_HOST }}.
Daniel Roseman,

29
Fare attenzione con l'utilizzo dei metadati di richiesta. Viene da un browser e può essere falsificato. In generale, probabilmente vorrai andare con quanto suggerito di seguito da @CarlMeyer.
Josh,

2
Per i miei scopi, questo non ha falle di sicurezza.
Paul Draper,

7
Immagino, dal momento che Django 1.5 con l'impostazione degli host consentiti è sicuro da usare. docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
Daniel Backman

8
Qualcuno può spiegare qual è il "buco di sicurezza"? Se l'utente falsifica l' Host:intestazione e riceve una risposta con il dominio contraffatto da qualche parte in una pagina, come può creare una falla nella sicurezza? Non vedo come questo differisca da un utente che prende l'HTML generato e si modifica prima di inviarlo al proprio browser.
user193130,

105

Se vuoi l'header HTTP Host attuale, vedi il commento di Daniel Roseman sulla risposta di @ Phsiao. L'altra alternativa è che se stai usando il framework contrib.sites , puoi impostare un nome di dominio canonico per un sito nel database (mappare il dominio di richiesta su un file di impostazioni con il SITE_ID corretto è qualcosa che devi fare tu stesso tramite il tuo configurazione del server web). In quel caso stai cercando:

from django.contrib.sites.models import Site

current_site = Site.objects.get_current()
current_site.domain

dovresti mettere tu stesso l'oggetto current_site in un contesto template se vuoi usarlo. Se lo stai usando ovunque, puoi impacchettarlo in un processore di contesto modello.


3
Per chiarire per qualcuno che ha gli stessi problemi che ho avuto: verifica che le tue SITE_IDimpostazioni siano uguali idall'attributo del sito corrente nell'app Sites (puoi trovare il idpannello di amministrazione di Sites). Quando chiami get_current, Django prende il tuo SITE_IDe restituisce l' Siteoggetto con quell'id dal database.
Dennis Golomazov,

Nessuno di questi funziona per me. print("get_current_site: ", get_current_site(request)) print("absolute uri: ", request.build_absolute_uri()) print("HTTP_HOST: ", request.META['HTTP_HOST']) get_current_site: localhost:8001 absolute uri: http://localhost:8001/... HTTP_HOST: localhost:8001
user251242

86

Ho scoperto il {{ request.get_host }}metodo.


11
Si noti che questa risposta presenta gli stessi problemi dell'approccio di Daniel Roseman (può essere falsificato) ma è sicuramente più completa quando l'host viene raggiunto tramite un proxy HTTP o un bilanciamento del carico poiché tiene conto dell'intestazione HTTP_X_FORWARDED_HOSTHTTP.
furins

4
Utilizzo: "// {{request.get_host}} / any / else / you / want" ... Assicurati di compilare l'impostazione ALLOWED_HOSTS (vedi docs.djangoproject.com/en/1.5/ref/settings/#allowed -host ).
Seth,

3
@Seth è meglio usare request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/… )
MrKsn

60

A completamento di Carl Meyer, puoi creare un processore di contesto come questo:

module.context_processors.py

from django.conf import settings

def site(request):
    return {'SITE_URL': settings.SITE_URL}

local settings.py

SITE_URL = 'http://google.com' # this will reduce the Sites framework db call.

settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    "module.context_processors.site",
    ....
 )

modelli che restituiscono l'istanza di contesto il sito dell'URL è {{SITE_URL}}

puoi scrivere la tua routine se vuoi gestire sottodomini o SSL nel processore di contesto.


Ho provato questa soluzione ma se hai diversi sottodomini per la stessa applicazione non è pratico, ho trovato molto utile la risposta di danbruegge
Jose Luis de la Rosa

in settings.py devi introdurre il tuo processore di contesto in context_processors> OPZIONI> MODELLI
yas17

24

La variazione del processore di contesto che utilizzo è:

from django.contrib.sites.shortcuts import get_current_site
from django.utils.functional import SimpleLazyObject


def site(request):
    return {
        'site': SimpleLazyObject(lambda: get_current_site(request)),
    }

Il SimpleLazyObjectwrapper si assicura che la chiamata DB avvenga solo quando il modello utilizza effettivamente l' siteoggetto. Ciò rimuove la query dalle pagine di amministrazione. Inoltre memorizza nella cache il risultato.

e includilo nelle impostazioni:

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    "module.context_processors.site",
    ....
)

Nel modello, è possibile utilizzare {{ site.domain }}per ottenere il nome di dominio corrente.

modifica: per supportare anche la commutazione del protocollo, utilizzare:

def site(request):
    site = SimpleLazyObject(lambda: get_current_site(request))
    protocol = 'https' if request.is_secure() else 'http'

    return {
        'site': site,
        'site_root': SimpleLazyObject(lambda: "{0}://{1}".format(protocol, site.domain)),
    }

Non è necessario utilizzarlo SimpleLazyObjectqui, perché lambda non verrà chiamato se nulla accede comunque al "sito".
Monokrome,

Se si rimuove SimpleLazyObject, ognuno RequestContextchiamerà get_current_site()e quindi eseguirà una query SQL. Il wrapper si assicura che la variabile venga valutata solo quando viene effettivamente utilizzata nel modello.
vdboor,

1
Poiché è una funzione, la stringa host non verrà elaborata a meno che non venga comunque utilizzata. Quindi, puoi semplicemente assegnare una funzione a 'site_root' e non hai bisogno di SimpleLazyObject. Django chiamerà la funzione quando viene utilizzata. Hai già creato la funzione necessaria con una lambda qui comunque.
Monokrome,

Ah sì, solo una lambda avrebbe funzionato. L' SimpleLazyObjectè lì per evitare di rivalutazione della funzione, che non è realmente necessario in quanto l' Siteoggetto viene memorizzato nella cache.
vdboor,

L'importazione è orafrom django.contrib.sites.shortcuts import get_current_site
Hraban,

22

So che questa domanda è vecchia, ma mi sono imbattuto in esso alla ricerca di un modo pitonico per ottenere il dominio corrente.

def myview(request):
    domain = request.build_absolute_uri('/')[:-1]
    # that will build the complete domain: http://foobar.com

4
build_absolute_uriè documentato qui .
Philipp Zedler,

19

Veloce e semplice, ma non buono per la produzione:

(in una vista)

    request.scheme               # http or https
    request.META['HTTP_HOST']    # example.com
    request.path                 # /some/content/1/

(in un modello)

{{ request.scheme }} :// {{ request.META.HTTP_HOST }} {{ request.path }}

Assicurati di usare un RequestContext , che è il caso se stai usando il rendering .

Non fidarti request.META['HTTP_HOST']della produzione: queste informazioni provengono dal browser. Usa invece la risposta di @ CarlMeyer


Sto votando questa risposta ma ho ricevuto un errore durante il tentativo di utilizzo request.scheme. Forse disponibile solo nelle versioni più recenti di django.
Matt Cremeens,

@MattCremeens è request.schemestato aggiunto in Django 1.7.
S. Kirby,

16

{{ request.get_host }}dovrebbe proteggere dagli attacchi di intestazione dell'host HTTP quando usato insieme ALLOWED_HOSTSall'impostazione (aggiunto in Django 1.4.4).

Si noti che {{ request.META.HTTP_HOST }}non ha la stessa protezione. Vedi i documenti :

allowed_hosts

Un elenco di stringhe che rappresentano i nomi host / dominio che questo sito Django può servire. Questa è una misura di sicurezza per prevenire attacchi di intestazione host HTTP , che sono possibili anche in molte configurazioni di server Web apparentemente sicure.

... Se l' Hostintestazione (o X-Forwarded-Hostse USE_X_FORWARDED_HOSTè abilitata) non corrisponde a nessun valore in questo elenco, il django.http.HttpRequest.get_host()metodo aumenterà SuspiciousOperation.

... Questa convalida si applica solo tramite get_host(); se il tuo codice accede all'intestazione Host direttamente da request.METAte, stai evitando questa protezione di sicurezza.


Per quanto riguarda l'utilizzo del requestmodello, le chiamate alla funzione di rendering del modello sono cambiate in Django 1.8 , quindi non è più necessario gestirle RequestContextdirettamente.

Ecco come eseguire il rendering di un modello per una vista, utilizzando la funzione di scelta rapida render():

from django.shortcuts import render

def my_view(request):
    ...
    return render(request, 'my_template.html', context)

Ecco come eseguire il rendering di un modello per un'email, quale IMO è il caso più comune in cui si desidera il valore host:

from django.template.loader import render_to_string

def my_view(request):
    ...
    email_body = render_to_string(
        'my_template.txt', context, request=request)

Ecco un esempio di aggiunta di un URL completo in un modello di posta elettronica; request.scheme dovrebbe ottenere httpo in httpsbase a cosa stai usando:

Thanks for registering! Here's your activation link:
{{ request.scheme }}://{{ request.get_host }}{% url 'registration_activate' activation_key %}

10

Uso un tag modello personalizzato. Aggiungi ad esempio <your_app>/templatetags/site.py:

# -*- coding: utf-8 -*-
from django import template
from django.contrib.sites.models import Site

register = template.Library()

@register.simple_tag
def current_domain():
    return 'http://%s' % Site.objects.get_current().domain

Usalo in un modello come questo:

{% load site %}
{% current_domain %}

C'è qualche particolare svantaggio di questo approccio? A parte la chiamata al Sito db su ogni richiesta.
kicker86

@ kicker86 Non ne conosco nessuno. get_currentè un metodo documentato: docs.djangoproject.com/en/dev/ref/contrib/sites/…
Dennis Golomazov

3
'http://%s'potrebbe essere un problema in caso di httpsconnessione; lo schema non è dinamico in questo caso.
Organico danneggiato

4

Simile alla risposta dell'utente Panchicore, questo è quello che ho fatto su un sito Web molto semplice. Fornisce alcune variabili e le rende disponibili nel modello.

SITE_URLconterrebbe un valore come example.com
SITE_PROTOCOLconterrebbe un valore come http o https
SITE_PROTOCOL_URLconterrebbe un valore come http://example.como https://example.com
SITE_PROTOCOL_RELATIVE_URLconterrebbe un valore come //example.com.

Modulo / context_processors.py

from django.conf import settings

def site(request):

    SITE_PROTOCOL_RELATIVE_URL = '//' + settings.SITE_URL

    SITE_PROTOCOL = 'http'
    if request.is_secure():
        SITE_PROTOCOL = 'https'

    SITE_PROTOCOL_URL = SITE_PROTOCOL + '://' + settings.SITE_URL

    return {
        'SITE_URL': settings.SITE_URL,
        'SITE_PROTOCOL': SITE_PROTOCOL,
        'SITE_PROTOCOL_URL': SITE_PROTOCOL_URL,
        'SITE_PROTOCOL_RELATIVE_URL': SITE_PROTOCOL_RELATIVE_URL
    }

settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    "module.context_processors.site",
    ....
 )

SITE_URL = 'example.com'

Poi, i modelli, li usano come {{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}e{{ SITE_PROTOCOL_RELATIVE_URL }}


2

In un modello Django puoi fare:

<a href="{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ request.path }}?{{ request.GET.urlencode }}" >link</a>

1
Questo ha funzionato per me grazie. Ho dovuto abilitare la richiesta in TEMPLATES, context_processors:, django.template.context_processors.requestanche [questo how-to aiutato] ( simpleisbetterthancomplex.com/tips/2016/07/20/… )
ionescu77

D'accordo, il blog Vitor Freitas è un'ottima fonte per gli sviluppatori di Django! :)
Dos

2

Se si utilizza il processore di contesto "richiesta" e si utilizza il framework dei siti Django e si dispone del middleware del sito installato (ovvero le impostazioni includono queste):

INSTALLED_APPS = [
    ...
    "django.contrib.sites",
    ...
]

MIDDLEWARE = [
    ...
     "django.contrib.sites.middleware.CurrentSiteMiddleware",
    ...
]

TEMPLATES = [
    {
        ...
        "OPTIONS": {
            "context_processors": [
                ...
                "django.template.context_processors.request",
                ...
            ]
        }
    }
]

... quindi avrai l' requestoggetto disponibile nei template e conterrà un riferimento alla corrente Siteper la richiesta come request.site. È quindi possibile recuperare il dominio in un modello con:

    {{request.site.domain}}

1

Che dire di questo approccio? Per me va bene. Viene anche usato nella registrazione django .

def get_request_root_url(self):
    scheme = 'https' if self.request.is_secure() else 'http'
    site = get_current_site(self.request)
    return '%s://%s' % (scheme, site)

Ma provarlo ti localhostdarà uno httpsschema (è considerato sicuro) che non funzionerà se hai l'URL statico (solo http://127.0.0.1è valido, non https://127.0.0.1). Quindi non è l'ideale quando è ancora in fase di sviluppo.
ThePhi

0
from django.contrib.sites.models import Site
if Site._meta.installed:
    site = Site.objects.get_current()
else:
    site = RequestSite(request)

-5

È possibile utilizzare {{ protocol }}://{{ domain }}nei modelli per ottenere il nome del dominio.


Non credo che @Erwan noti che questo dipende da un processore di contesto di richiesta non standard.
Monokrome,

Non ho potuto farlo funzionare, dove definisci il protocollo e il dominio?
Jose Luis de la Rosa
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.