Como posso obter o nome de domínio do meu site dentro de um modelo do Django?


Respostas:


67

Eu acho que o que você quer é ter acesso ao contexto da solicitação, consulte RequestContext.


140
request.META['HTTP_HOST']fornece o domínio. Em um modelo seria {{ request.META.HTTP_HOST }}.
21139 Daniel Roseman

29
Cuidado ao usar os metadados da solicitação. É proveniente de um navegador e pode ser falsificado. Em geral, você provavelmente vai querer seguir o que é sugerido abaixo por @CarlMeyer.
217 Josh

2
Para meus propósitos, isso não tem brechas de segurança.
Paul Draper

7
Eu acho que desde o Django 1.5 com a configuração de hosts permitidos é seguro usar. docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
Daniel Backman

8
Alguém pode elaborar o que é a "brecha de segurança"? Se o usuário falsifica o Host:cabeçalho e recebe uma resposta de volta com o domínio falsificado em algum lugar da página, como isso cria uma brecha na segurança? Não vejo como isso difere de um usuário pegar o HTML gerado e se modificar antes de alimentá-lo em seu próprio navegador.
User193130

105

Se você deseja o cabeçalho HTTP Host real, consulte o comentário de Daniel Roseman na resposta de @ Phsiao. A outra alternativa é que, se você estiver usando a estrutura contrib.sites , poderá definir um nome de domínio canônico para um Site no banco de dados (mapear o domínio de solicitação para um arquivo de configurações com o SITE_ID adequado é algo que você deve fazer através do seu configuração do servidor da web). Nesse caso, você está procurando:

from django.contrib.sites.models import Site

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

você teria que colocar o objeto current_site em um contexto de modelo, se quiser usá-lo. Se você estiver usando tudo isso em qualquer lugar, poderá empacotar isso em um processador de contexto de modelo.


3
Para esclarecer para alguém que tem os mesmos problemas que eu tive: verifique se sua SITE_IDconfiguração é igual ao idatributo do site atual no aplicativo Sites (você pode encontrar idno painel de administração de Sites). Quando você chama get_current, o Django pega o seu SITE_IDe retorna o Siteobjeto com esse ID do banco de dados.
Dennis Golomazov 01/07/2013

Nada disso funciona para mim. 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

Eu descobri o {{ request.get_host }}método.


11
Observe que esta resposta tem os mesmos problemas da abordagem de Daniel Roseman (pode ser falsificada), mas certamente é mais completa quando o host é alcançado através de um proxy HTTP ou balanceador de carga, pois leva em consideração o HTTP_X_FORWARDED_HOSTcabeçalho HTTP.
97714

4
Uso: "// {{request.get_host}} / qualquer coisa / else / you / want" ... Certifique-se de preencher sua configuração ALLOWED_HOSTS (consulte docs.djangoproject.com/en/1.5/ref/settings/#allowed -hosts ).
Seth

3
@Seth melhor uso request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/... )
MrKsn

60

Complementando Carl Meyer, você pode criar um processador de contexto como este:

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

modelos retornando instância de contexto em que o site da URL é {{SITE_URL}}

você pode escrever sua própria rotina se desejar manipular subdomínios ou SSL no processador de contexto.


Eu tentei esta solução, mas se você tiver vários subdomínios para a mesma aplicação, não é prático, eu achei muito útil a resposta por danbruegge
Jose Luis de la Rosa

em settings.py, você deve apresentar seu processador de contexto em context_processors> OPTIONS> TEMPLATES
yas17 13/01

24

A variação do processador de contexto que eu uso é:

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

O SimpleLazyObjectwrapper garante que a chamada ao banco de dados ocorra apenas quando o modelo realmente usa o siteobjeto. Isso remove a consulta das páginas de administração. Ele também armazena em cache o resultado.

e inclua-o nas configurações:

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

No modelo, você pode usar {{ site.domain }}para obter o nome de domínio atual.

edit: para suportar também a troca de protocolo, 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)),
    }

Você não precisa usar SimpleLazyObjectaqui, porque o lambda não será chamado se nada acessar o 'site' de qualquer maneira.
monokrome

Se você remover o SimpleLazyObject, cada RequestContextum chamará get_current_site()e, portanto, executará uma consulta SQL. O wrapper garante que a variável seja avaliada apenas quando for realmente usada no modelo.
vdboor

1
Por ser uma função, a cadeia do host não será processada, a menos que seja usada de qualquer maneira. Portanto, você pode apenas atribuir uma função ao 'site_root' e não precisa do SimpleLazyObject. O Django chamará a função quando for usada. Você já criou a função necessária com um lambda aqui de qualquer maneira.
monokrome

Ah sim, apenas um lambda funcionaria. O SimpleLazyObjectque há para evitar a reavaliação da função, que não é realmente necessário uma vez que o Siteobjeto é armazenado em cache.
vdboor

A importação agora éfrom django.contrib.sites.shortcuts import get_current_site
Hraban

22

Sei que essa pergunta é antiga, mas me deparei com ela procurando uma maneira pitônica de obter o domínio atual.

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

4
build_absolute_uriestá documentado aqui .
Philipp Zedler 10/09

19

Rápido e simples, mas não bom para produção:

(em uma exibição)

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

(em um modelo)

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

Certifique-se de usar um RequestContext , que é o caso se você estiver usando renderização .

Não confie request.META['HTTP_HOST']na produção: essas informações vêm do navegador. Em vez disso, use a resposta do @ CarlMeyer


Estou com o voto positivo desta resposta, mas recebi um erro ao tentar usar request.scheme. Talvez apenas disponível em versões mais recentes do django.
214188 Matt Mattens #

O @MattCremeens request.schemefoi adicionado no Django 1.7.
S. Kirby

16

{{ request.get_host }}deve proteger contra ataques de cabeçalho do host HTTP quando usado junto com a ALLOWED_HOSTSconfiguração (adicionada no Django 1.4.4).

Observe que {{ request.META.HTTP_HOST }}não possui a mesma proteção. Veja os documentos :

ALLOWED_HOSTS

Uma lista de strings representando os nomes de host / domínio que este site do Django pode servir. Essa é uma medida de segurança para impedir ataques ao cabeçalho do host HTTP , que são possíveis mesmo em muitas configurações de servidor da Web aparentemente seguras.

... Se o Hostcabeçalho (ou X-Forwarded-Hostse USE_X_FORWARDED_HOSTestiver ativado) não corresponder a nenhum valor nesta lista, o django.http.HttpRequest.get_host()método aumentará SuspiciousOperation.

... Esta validação se aplica apenas a get_host(); se o seu código acessar o cabeçalho do host diretamente de request.METAvocê, está ignorando essa proteção de segurança.


Quanto ao uso do requestem seu modelo, as chamadas de função de renderização de modelo foram alteradas no Django 1.8 , assim você não precisa mais lidar RequestContextdiretamente.

Veja como renderizar um modelo para uma visualização, usando a função de atalho render():

from django.shortcuts import render

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

Veja como renderizar um modelo para um email, qual IMO é o caso mais comum em que você deseja o valor do host:

from django.template.loader import render_to_string

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

Aqui está um exemplo de adição de um URL completo em um modelo de email; request.scheme deve obter httpou httpsdependendo do que você está usando:

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

10

Eu uso uma tag de modelo personalizado. Adicione, por exemplo <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

Use-o em um modelo como este:

{% load site %}
{% current_domain %}

Existe alguma desvantagem específica nessa abordagem? Além da chamada para o site db em cada solicitação.
Kicker86 4/04

@ kicker86 Eu não conheço nenhum. get_currenté um método documentado: docs.djangoproject.com/en/dev/ref/contrib/sites/…
Dennis Golomazov 13/15

3
'http://%s'pode ser um problema em caso de httpsconexão; esquema não é dinâmico neste caso.
Organic Danificado

4

Semelhante à resposta do usuário panchicore, foi o que fiz em um site muito simples. Ele fornece algumas variáveis ​​e as disponibiliza no modelo.

SITE_URLmanteria um valor como example.com
SITE_PROTOCOLmanteria um valor como http ou https
SITE_PROTOCOL_URLmanteria um valor como http://example.comou https://example.com
SITE_PROTOCOL_RELATIVE_URLmanteria um 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'

Então, em seus modelos, usá-los como {{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}e{{ SITE_PROTOCOL_RELATIVE_URL }}


2

Em um modelo do Django, você pode fazer:

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

1
Isso funcionou para mim, obrigado. Eu tinha que permitir solicitação na MODELOS, context_processors: django.template.context_processors.request, também [este how-to ajudou] ( simpleisbetterthancomplex.com/tips/2016/07/20/... )
ionescu77

Concordo, o blog Vitor Freitas é uma ótima fonte para desenvolvedores do Django! :)
Dos

2

Se você usa o processador de contexto "request" e está usando a estrutura de sites do Django e possui o middleware Site instalado (ou seja, suas configurações incluem estas):

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

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

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

... então você terá o requestobjeto disponível nos modelos e ele conterá uma referência à corrente Sitepara a solicitação como request.site. Você pode recuperar o domínio em um modelo com:

    {{request.site.domain}}

1

E essa abordagem? Funciona para mim. Também é usado no django-registration .

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)

Mas tentar com localhostisso fornecerá um httpsesquema (é considerado seguro) que não funcionará se você tiver um URL estático (apenas http://127.0.0.1válido, não https://127.0.0.1). Portanto, não é o ideal quando ainda está em desenvolvimento.
ThePhi

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

-5

Você pode usar {{ protocol }}://{{ domain }}em seus modelos para obter seu nome de domínio.


Não acho que o @Erwan perceba que isso depende de um processador de contexto de solicitação não padrão.
monokrome

Não pude fazer esse trabalho. Onde você define protocolo e domínio?
Jose Luis de la Rosa
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.