如何在Django模板中获取网站的域名?


Answers:


67

我认为您想要的是可以访问请求上下文,请参见RequestContext。


140
request.META['HTTP_HOST']给您域名。在模板中应该是{{ request.META.HTTP_HOST }}
丹尼尔·罗斯曼

29
使用请求元数据时要小心。它来自浏览器,可以被欺骗。通常,您可能想要使用@CarlMeyer在下面建议的内容。
乔什(Josh)2012年

2
就我而言,这没有安全漏洞。
Paul Draper 2013年

7
我猜,由于Django 1.5设置了允许的主机,因此可以安全使用。docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
Daniel Backman

8
有人可以详细说明什么是“安全漏洞”吗?如果用户欺骗Host:标头并在页面上某处获得带有欺骗域的响应,那将如何创建安全漏洞?我看不出这与用户采用生成的HTML并在将其馈送到其自己的浏览器之前对其进行修改之前不同。
user193130 '16

105

如果需要实际的HTTP Host标头,请参阅Daniel Roseman对@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_IDSite从数据库返回具有该ID 的对象。
丹尼斯·哥洛马佐夫

这些都不适合我。 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
请注意,此答案与Daniel Roseman方法具有相同的问题(可以被欺骗),但是通过HTTP代理或负载平衡器访问主机时,由于它考虑了HTTP_X_FORWARDED_HOSTHTTP标头,因此肯定更完整。
furins 2014年

4
用法:“ // {{request.get_host}} /任何东西/其他/您/想要” ...请务必填写您的ALLOWED_HOSTS设置(请参阅docs.djangoproject.com/zh/1.5/ref/settings/#allowed -hosts)。
赛斯2014年

3
@Seth更好的使用request.build_absolute_uridocs.djangoproject.com/en/dev/ref/request-response/...
MrKsn

60

作为对Carl Meyer的补充,您可以像这样创建一个上下文处理器:

module.context_processors.py

from django.conf import settings

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

本地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的答案非常有用
Jose Luis de la Rosa

在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 }}用来获取当前域名。

编辑:也要支持协议切换,请使用:

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这里使用,因为如果没有人访问“站点”,则不会调用lambda。
monokrome

如果删除SimpleLazyObject,每个RequestContext将调用get_current_site(),并因此执行SQL查询。包装器确保仅在模板中实际使用变量时才评估该变量。
vdboor

1
由于它是一个函数,因此除非使用主机字符串,否则不会对其进行处理。因此,您只需将一个函数分配给'site_root',就不需要SimpleLazyObject。Django将在使用该函数时调用该函数。无论如何,您已经使用lambda创建了必要的功能。
monokrome

是的,只有lambda可以工作。的SimpleLazyObject目的是避免的功能,这是不是真的需要,因为重新评估Site对象缓存。
vdboor 2014年

现在是进口from django.contrib.sites.shortcuts import get_current_site
Hraban

22

我知道这个问题很老,但是我偶然发现了这个问题,寻找一种获取当前域的pythonic方法。

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

4
build_absolute_uri记录在这里
Philipp Zedler 2015年

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中添加的。
S. Kirby '18

16

{{ request.get_host }}ALLOWED_HOSTS设置(在Django 1.4.4中添加)一起使用时,应该可以防止HTTP Host标头攻击。

请注意,{{ request.META.HTTP_HOST }}没有相同的保护。见文档

ALLOWED_HOSTS

代表此Django站点可以服务的主机/域名的字符串列表。这是一种安全措施,可以防止HTTP Host标头攻击,即使在许多看似安全的Web服务器配置下也可能发生这种攻击

...如果Host标头(或者X-Forwarded-Host如果USE_X_FORWARDED_HOST使能)不匹配,在此列表中的任何值,该django.http.HttpRequest.get_host()方法将提高SuspiciousOperation

...此验证仅通过进行get_host();如果您的代码直接从request.META您访问Host标头,则绕过此安全保护措施。


至于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应该获得httphttps取决于您使用的是什么:

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 %}

这种方法有什么不利之处吗?除了根据每个请求调用Site db之外。
kicker86

@ kicker86我什么都不知道。get_current是一种记录方法:docs.djangoproject.com/en/dev/ref/contrib/sites/...
丹尼斯Golomazov

3
'http://%s'https连接的情况下可能是一个问题;在这种情况下,方案不是动态的。
损坏的有机

4

与用户panchicore的回复类似,这是我在一个非常简单的网站上所做的。它提供了一些变量并使它们在模板上可用。

SITE_URL将举行一个类似的值example.com
SITE_PROTOCOL将举行类似的值httphttps
SITE_PROTOCOL_URL将举行类似的值http://example.comhttps://example.com
SITE_PROTOCOL_RELATIVE_URL将持有的值等//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'

然后,在你的模板,把它们作为{{ 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开发人员来说是一个很好的资源!:)
Dos

2

如果您使用“请求”上下文处理器,并且正在使用Django sites框架,并且安装了Site中间件(即您的设置包括以下内容):

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

但是尝试使用它localhost会为您提供一个https方案(它被认为是安全的),如果您具有静态url(仅http://127.0.0.1有效,则不是https://127.0.0.1),该方案将不起作用。因此,当仍在开发中时,它并不理想。
ThePhi


-5

您可以{{ protocol }}://{{ domain }}在模板中使用来获取域名。


我认为@Erwan不会注意到这取决于非标准的请求上下文处理器。
monokrome 2014年

我无法完成这项工作,您在哪里定义协议和域?
Jose Luis de la Rosa
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.