¿Cómo puedo obtener el nombre de dominio de mi sitio dentro de una plantilla de Django?

156

¿Cómo obtengo el nombre de dominio de mi sitio actual desde una plantilla de Django? He intentado buscar en la etiqueta y los filtros, pero no hay nada allí.

Jean-François Fabre
fuente

Respuestas:

67

Creo que lo que quiere es tener acceso al contexto de solicitud, consulte RequestContext.

phsiao
fuente
140
request.META['HTTP_HOST']te da el dominio En una plantilla sería {{ request.META.HTTP_HOST }}.
Daniel Roseman
29
Tenga cuidado con el uso de metadatos de solicitud. Proviene de un navegador y puede ser falsificado. En general, es probable que desee seguir lo que sugiere @CarlMeyer a continuación.
Josh
2
Para mis propósitos, esto no tiene ningún agujero de seguridad.
Paul Draper
77
Supongo que desde Django 1.5 con la configuración de hosts permitidos es seguro de usar. docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
Daniel Backman
8
¿Alguien puede explicar qué es el "agujero de seguridad"? Si el usuario falsifica el Host:encabezado y obtiene una respuesta con el dominio falsificado en algún lugar de una página, ¿cómo crea eso un agujero de seguridad? No veo cómo eso difiere de que un usuario tome el HTML generado y se modifique antes de alimentarlo a su propio navegador.
user193130
105

Si desea el encabezado del host HTTP real, vea el comentario de Daniel Roseman sobre la respuesta de @ Phsiao. La otra alternativa es que si está utilizando el marco contrib.sites , puede establecer un nombre de dominio canónico para un Sitio en la base de datos (asignar el dominio de solicitud a un archivo de configuración con el SITE_ID adecuado es algo que debe hacer usted mismo a través de su configuración del servidor web). En ese caso estás buscando:

from django.contrib.sites.models import Site

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

tendría que poner el objeto current_site en un contexto de plantilla usted mismo si desea usarlo. Si lo está utilizando por todas partes, puede empaquetarlo en un procesador de contexto de plantilla.

Carl Meyer
fuente
3
Para aclarar a alguien que tiene los mismos problemas que yo: verifique que su SITE_IDconfiguración sea igual al idatributo del sitio actual en la aplicación Sitios (puede encontrarlo iden el panel de administración de Sitios). Cuando llamas get_current, Django toma tu SITE_IDy devuelve el Siteobjeto con esa identificación de la base de datos.
Dennis Golomazov
Nada de esto funciona para mí. 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

He descubierto el {{ request.get_host }}método.

danbruegge
fuente
11
Tenga en cuenta que esta respuesta tiene los mismos problemas que el enfoque de Daniel Roseman (puede ser falso), pero seguramente es más completa cuando se llega al host a través de un proxy HTTP o un equilibrador de carga, ya que tiene en cuenta el HTTP_X_FORWARDED_HOSTencabezado HTTP.
Furins
44
Uso: "// {{request.get_host}} / anything / else / you / want" ... Asegúrese de completar su configuración ALLOWED_HOSTS (consulte docs.djangoproject.com/en/1.5/ref/settings/#allowed -hosts ).
Seth
3
@Seth mejor usar request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/… )
MrKsn
60

Complementando a Carl Meyer, puede hacer un procesador de contexto como este:

module.context_processors.py

from django.conf import settings

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

configuración local.py

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

settings.py

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

plantillas que devuelven la instancia de contexto, el sitio de URL es {{SITE_URL}}

puede escribir su propia rutina si desea manejar subdominios o SSL en el procesador de contexto.

panchicore
fuente
Probé esta solución, pero si tiene varios subdominios para la misma aplicación, no es práctica, la respuesta de danbruegge me pareció muy útil
José Luis de la Rosa,
en settings.py debe introducir su procesador de contexto en context_processors> OPCIONES> PLANTILLAS
yas17
24

La variación del procesador de contexto que uso es:

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

El SimpleLazyObjectcontenedor se asegura de que la llamada a la base de datos solo ocurra cuando la plantilla realmente use el siteobjeto. Esto elimina la consulta de las páginas de administración. También almacena en caché el resultado.

e inclúyelo en la configuración:

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

En la plantilla, puede usar {{ site.domain }}para obtener el nombre de dominio actual.

editar: para admitir el cambio de protocolo también, use:

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)),
    }
vdboor
fuente
No es necesario usarlo SimpleLazyObjectaquí, porque de todos modos no se llamará a lambda si nada accede al "sitio".
monokrome
Si elimina el SimpleLazyObject, cada RequestContextllamará get_current_site()y, por lo tanto, ejecutará una consulta SQL. El contenedor se asegura de que la variable solo se evalúe cuando realmente se usa en la plantilla.
vdboor
1
Como es una función, la cadena de host no se procesará a menos que se use de todos modos. Por lo tanto, puede asignar una función a 'site_root' y no necesita SimpleLazyObject. Django llamará a la función cuando se use. Ya has creado la función necesaria con una lambda aquí de todos modos.
monokrome
Ah sí, solo una lambda funcionaría. El SimpleLazyObjectestá ahí para evitar la re-evaluación de la función, que no es realmente necesario ya que el Sitese almacena en caché de objetos.
vdboor
La importación es ahorafrom django.contrib.sites.shortcuts import get_current_site
Hraban
22

Sé que esta pregunta es antigua, pero me topé con ella buscando una forma pitónica para obtener el dominio actual.

def myview(request):
    domain = request.build_absolute_uri('/')[:-1]
    # that will build the complete domain: http://foobar.com
misterte
fuente
44
build_absolute_uriestá documentado aquí .
Philipp Zedler
19

Rápido y simple, pero no es bueno para la producción:

(en una vista)

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

(en una plantilla)

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

Asegúrese de utilizar RequestContext , que es el caso si está utilizando render .

No confíes request.META['HTTP_HOST']en la producción: esa información proviene del navegador. En cambio, use la respuesta de @ CarlMeyer

Edward Newell
fuente
Estoy votando esta respuesta pero recibí un error al intentar usar request.scheme. Quizás solo esté disponible en versiones más recientes de django.
Matt Cremeens
@MattCremeens request.schemese agregó en Django 1.7.
S. Kirby
16

{{ request.get_host }}debería proteger contra ataques de encabezado de host HTTP cuando se usa junto con la ALLOWED_HOSTSconfiguración (agregado en Django 1.4.4).

Tenga en cuenta que {{ request.META.HTTP_HOST }}no tiene la misma protección. Ver los documentos :

ALLOWED_HOSTS

Una lista de cadenas que representan los nombres de host / dominio que este sitio de Django puede servir. Esta es una medida de seguridad para evitar ataques de encabezado de host HTTP , que son posibles incluso bajo muchas configuraciones de servidor web aparentemente seguras.

... Si el Hostencabezado (o X-Forwarded-Hostsi USE_X_FORWARDED_HOSTestá habilitado) no coincide con ningún valor en esta lista, el django.http.HttpRequest.get_host()método aumentará SuspiciousOperation.

... Esta validación solo se aplica a través de get_host(); si su código accede al encabezado del Host directamente desde request.METAusted, está pasando por alto esta protección de seguridad.


En cuanto al uso de requesten su plantilla, las llamadas a la función de representación de plantilla han cambiado en Django 1.8 , por lo que ya no tiene que manejar RequestContextdirectamente.

Aquí se explica cómo representar una plantilla para una vista, utilizando la función de acceso directo render():

from django.shortcuts import render

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

A continuación, le mostramos cómo representar una plantilla para un correo electrónico, cuyo IMO es el caso más común en el que desea el valor de host:

from django.template.loader import render_to_string

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

Aquí hay un ejemplo de cómo agregar una URL completa en una plantilla de correo electrónico; request.scheme debería obtener httpo, httpssegún lo que esté usando:

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

Yo uso una etiqueta de plantilla personalizada. Agregar a, por ejemplo <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

Úselo en una plantilla como esta:

{% load site %}
{% current_domain %}
Dennis Golomazov
fuente
¿Hay algún inconveniente particular en este enfoque? Aparte de la llamada al sitio db en cada solicitud.
kicker86
@ kicker86 No sé ninguno. get_currentes un método documentado: docs.djangoproject.com/en/dev/ref/contrib/sites/…
Dennis Golomazov
3
'http://%s'podría ser un problema en caso de httpsconexión; El esquema no es dinámico en este caso.
Dañado Organic
4

Similar a la respuesta del usuario panchicore, esto es lo que hice en un sitio web muy simple. Proporciona algunas variables y las pone a disposición en la plantilla.

SITE_URLmantendría un valor como example.com
SITE_PROTOCOLmantendría un valor como http o https
SITE_PROTOCOL_URLmantendría un valor como http://example.como https://example.com
SITE_PROTOCOL_RELATIVE_URLmantendría un valor como//example.com .

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

Luego, en sus plantillas, los utilizan como {{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}y{{ SITE_PROTOCOL_RELATIVE_URL }}

Julián Landerreche
fuente
2

En una plantilla de Django puedes hacer:

<a href="{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ request.path }}?{{ request.GET.urlencode }}" >link</a>
Dos
fuente
1
Esto funcionó para mí, gracias. Tuve que habilitar la solicitud en PLANTILLAS, context_processors:, django.template.context_processors.requesttambién [este tutorial ayudó] ( simpleisbetterthancomplex.com/tips/2016/07/20/… )
ionescu77
¡De acuerdo, el blog de Vitor Freitas es una gran fuente para los desarrolladores de Django! :)
Dos
2

Si utiliza el procesador de contexto de "solicitud" , y está utilizando el marco de sitios de Django , y tiene instalado el middleware del sitio (es decir, su configuración incluye estos):

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

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

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

... entonces tendrá el requestobjeto disponible en plantillas, y contendrá una referencia a la actual Sitepara la solicitud como request.site. Luego puede recuperar el dominio en una plantilla con:

    {{request.site.domain}}
usuario85461
fuente
1

¿Qué hay de este enfoque? Funciona para mi. También se usa en el registro de 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)

fuente
Pero probarlo te localhostdará un httpsesquema (se considera seguro) que no funcionará si tienes una URL estática (solo http://127.0.0.1es válida, no https://127.0.0.1). Por lo tanto, no es ideal cuando todavía está en desarrollo.
ThePhi
0
from django.contrib.sites.models import Site
if Site._meta.installed:
    site = Site.objects.get_current()
else:
    site = RequestSite(request)
Muneeb Ahmad
fuente
-5

Puede usar {{ protocol }}://{{ domain }}en sus plantillas para obtener su nombre de dominio.

Erwan
fuente
No creo que @Erwan advierta que esto depende de un procesador de contexto de solicitud no estándar.
Monokrome
No podría hacer que esto funcione, ¿dónde define el protocolo y el dominio?
José Luis de la Rosa