Как я могу получить доменное имя моего сайта в шаблоне 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.