Как я могу получить доменное имя моего сайта в шаблоне Django?


Ответы:


67

Я думаю, что вы хотите, чтобы иметь доступ к контексту запроса, см. RequestContext.


140
request.META['HTTP_HOST']дает вам домен. В шаблоне это было бы {{ request.META.HTTP_HOST }}.
Даниэль Роузман

29
Будьте осторожны с использованием метаданных запроса. Это происходит из браузера и может быть подделано. В общем, вы, вероятно, захотите придерживаться того, что предложено ниже @CarlMeyer.
Джош

2
Для моих целей это не имеет дыры в безопасности.
Пол Дрейпер,

7
Я предполагаю, что начиная с Django 1.5 с разрешенными настройками хостов, это безопасно для использования. docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
Даниэль Бэкман,

8
Может кто-нибудь уточнить, что такое «дыра в безопасности»? Если пользователь подделывает Host:заголовок и получает ответ с поддельным доменом где-то на странице, как это создает дыру в безопасности? Я не понимаю, чем это отличается от того, что пользователь берет сгенерированный HTML и модифицирует себя перед тем, как передать его в свой собственный браузер.
user193130

105

Если вам нужен фактический заголовок HTTP Host, см. Комментарий Даниэля Роземана к ответу @ Phsiao. Другой вариант - если вы используете платформу contrib.sites , вы можете установить каноническое доменное имя для сайта в базе данных (сопоставление домена запроса с файлом настроек с правильным SITE_ID - это то, что вы должны сделать самостоятельно через свой настройка веб-сервера). В этом случае вы ищете:

from django.contrib.sites.models import Site

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

вам нужно будет поместить объект current_site в контекст шаблона самостоятельно, если вы хотите его использовать. Если вы используете его повсеместно, вы можете упаковать его в процессор контекста шаблона.


3
Чтобы выяснить, для кого возникают те же проблемы, что и у меня: проверьте, что ваши SITE_IDнастройки соответствуют idатрибуту текущего сайта в приложении «Сайты» (вы можете найти его idв админ-панели «Сайты»). Когда вы звоните get_current, Django берет ваш SITE_IDи возвращает Siteобъект с этим идентификатором из базы данных.
Денис Голомазов

Ничто из этого не работает для меня. 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

Я открыл {{ request.get_host }}метод.


11
Обратите внимание, что в этом ответе есть те же проблемы, что и в подходе Даниэля Розмана (его можно подделать), но он, безусловно, более полон, когда к хосту обращаются через HTTP-прокси или балансировщик нагрузки, поскольку он учитывает HTTP_X_FORWARDED_HOSTзаголовок HTTP.
Фуринс

4
Использование: "// {{request.get_host}} / что-нибудь / еще / вы / хотите" ... Обязательно заполните настройку ALLOWED_HOSTS (см. Docs.djangoproject.com/en/1.5/ref/settings/#allowed -хосты ).
Сет

3
@ Лучше использовать request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/… )
MrKsn

60

В дополнение к Карлу Мейеру, вы можете создать контекстный процессор, например так:

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

шаблоны, возвращающие экземпляр контекста, URL-адрес сайта {{SITE_URL}}

Вы можете написать свою собственную рутину, если хотите обрабатывать субдомены или SSL в контекстном процессоре.


Я попробовал это решение, но если у вас есть несколько поддоменов для одного и того же приложения, это не практично, я нашел очень полезный ответ от danbruegge
Хосе Луис де ла Роса

в settings.py вы должны представить свой контекстный процессор в context_processors> OPTIONS> TEMPLATES
yas17

24

Вариант контекстного процессора, который я использую:

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

SimpleLazyObjectОбертка убеждается вызов DB происходит только тогда , когда шаблон фактически использует siteобъект. Это удаляет запрос со страниц администратора. Это также кеширует результат.

и включите его в настройках:

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

В шаблоне вы можете использовать {{ site.domain }}для получения текущего доменного имени.

edit: для поддержки переключения протокола тоже используйте:

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

Вам не нужно использовать SimpleLazyObjectздесь, потому что лямбда не будет вызываться, если в любом случае ничто не заходит на сайт.
монокром

Если вы удалите SimpleLazyObject, каждый RequestContextбудет вызывать get_current_site(), и, следовательно, выполнить запрос SQL. Оболочка гарантирует, что переменная оценивается только тогда, когда она фактически используется в шаблоне.
vdboor

1
Поскольку это функция, строка хоста не будет обработана, если она не используется в любом случае. Таким образом, вы можете просто назначить функцию для site_root, и вам не нужен SimpleLazyObject. Django вызовет функцию, когда она будет использована. В любом случае вы уже создали необходимую функцию с лямбдой здесь.
монокром

Ах да, только лямбда будет работать. Это SimpleLazyObjectсделано для того, чтобы избежать переоценки функции, которая на самом деле не нужна, поскольку Siteобъект кэшируется.
vdboor

Импорт сейчасfrom django.contrib.sites.shortcuts import get_current_site
Hraban

22

Я знаю, что этот вопрос старый, но я наткнулся на него в поисках питонского способа получения текущего домена.

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

4
build_absolute_uri задокументировано здесь .
Филипп Зедлер

19

Быстро и просто, но не подходит для производства:

(в представлении)

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

(в шаблоне)

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

Обязательно используйте RequestContext , который имеет место, если вы используете render .

Не доверяйте request.META['HTTP_HOST']производству: эта информация поступает из браузера. Вместо этого используйте ответ @ CarlMeyer


Я отвечаю за этот ответ, но при попытке использования я получил ошибку request.scheme. Возможно, доступно только в новых версиях Django.
Мэтт Кременс,

@MattCremeens request.schemeбыл добавлен в Django 1.7.
С. Кирби

16

{{ request.get_host }}должен защищать от атак заголовка HTTP-хоста при использовании вместе с ALLOWED_HOSTSнастройкой (добавлено в Django 1.4.4).

Обратите внимание, что {{ request.META.HTTP_HOST }}не имеет такой же защиты. Смотрите документы :

allowed_hosts

Список строк, представляющих имена хостов / доменов, которые может обслуживать этот сайт Django. Это мера безопасности для предотвращения атак заголовка HTTP-хоста , что возможно даже при многих, казалось бы, безопасных конфигурациях веб-сервера.

... Если в Hostзаголовке (или X-Forwarded-Hostесли USE_X_FORWARDED_HOSTвключен) , не соответствует какому - либо значению в этом списке, то django.http.HttpRequest.get_host()метод будет поднимать SuspiciousOperation.

... Эта проверка применяется только через get_host(); если ваш код получает доступ к заголовку хоста непосредственно от request.METAвас, обходят эту защиту.


Что касается использования requestв вашем шаблоне, вызовы функции рендеринга шаблонов изменились в Django 1.8 , поэтому вам больше не нужно обрабатывать RequestContextнапрямую.

Вот как сделать шаблон для представления, используя функцию ярлыка render():

from django.shortcuts import render

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

Вот как вывести шаблон для электронного письма, в котором IMO является наиболее распространенным случаем, когда вы хотите указать значение хоста:

from django.template.loader import render_to_string

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

Вот пример добавления полного URL в шаблон электронной почты; request.scheme должен получить httpили в httpsзависимости от того, что вы используете:

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

10

Я использую пользовательский тег шаблона. Добавить к например <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

Используйте его в таком шаблоне:

{% load site %}
{% current_domain %}

Есть ли какой-то конкретный недостаток в этом подходе? Помимо звонка на сайт БД по каждому запросу.
kicker86

@ kicker86 Я не знаю ни одного. get_currentэто документированный метод: docs.djangoproject.com/en/dev/ref/contrib/sites/…
Деннис Голомазов

3
'http://%s'может быть проблема в случае httpsподключения; Схема не является динамической в ​​этом случае.
Поврежденный органический

4

Подобно ответу пользователя panchicore, это то, что я сделал на очень простом веб-сайте. Он предоставляет несколько переменных и делает их доступными в шаблоне.

SITE_URLбудет содержать значение like example.com
SITE_PROTOCOLбудет содержать значение like http или https
SITE_PROTOCOL_URLбудет содержать значение like http://example.comили https://example.com
SITE_PROTOCOL_RELATIVE_URLбудет содержать значение like //example.com.

модуль / 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'

Затем на шаблонах, использовать их как {{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}и{{ SITE_PROTOCOL_RELATIVE_URL }}


2

В шаблоне Django вы можете сделать:

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

1
Это сработало для меня, спасибо. Мне нужно было включить запрос в шаблонах, context_processors:, django.template.context_processors.requestтакже [это руководство помогло] ( simpleisbetterthancomplex.com/tips/2016/07/20/… )
ionescu77

Согласитесь, блог Vitor Freitas - отличный источник информации для разработчиков Django! :)
Дос

2

Если вы используете обработчик контекста «запрос» и используете платформу сайтов Django , и у вас установлено промежуточное программное обеспечение сайта (т.е. ваши настройки включают это):

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

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

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

... тогда у вас будет requestобъект, доступный в шаблонах, и он будет содержать ссылку на текущий Siteдля запроса как request.site. Затем вы можете получить домен в шаблоне с помощью:

    {{request.site.domain}}

1

Как насчет этого подхода? Работает для меня. Он также используется в 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)

Но если вы попробуете это, localhostвы получите httpsсхему (она считается безопасной), которая не будет работать, если у вас есть статический URL (только http://127.0.0.1действительный, а не https://127.0.0.1). Так что это не идеально, когда все еще в разработке.
ThePhi


-5

Вы можете использовать {{ protocol }}://{{ domain }}в своих шаблонах, чтобы получить доменное имя.


Я не думаю, что @Erwan замечает, что это зависит от нестандартного обработчика контекста запроса.
монокром

Я не мог сделать эту работу, где вы определяете протокол и домен?
Хосе Луис де ла Роса
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.