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

如何从Django模板中获取当前站点的域名?我试着在标签和过滤器中寻找,但没有什么。

201514 次浏览

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

如果您想要实际的HTTP Host报头,请参阅Daniel Roseman对@Phsiao的回答的评论。另一种替代方法是如果你使用普通发布版。网站框架,你可以在数据库中为站点设置一个规范的域名(将请求域映射到具有正确SITE_ID的设置文件是你必须通过你的web服务器设置自己做的事情)。在这种情况下,你要寻找:

from django.contrib.sites.models import Site


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

如果您想使用current_site对象,则必须自己将其放入模板上下文中。如果你到处都在使用它,你可以把它打包到一个模板上下文处理器中。

补充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,可以编写自己的例程。

我使用的上下文处理器的变化是:

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包装器确保只在模板实际使用site对象时才发生DB调用。这将从管理页面中删除查询。它还缓存结果。

并将其包含在设置中:

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

我发现了\{\{ request.get_host }}方法。

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

我使用自定义模板标记。添加到e.g. <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 %}
from django.contrib.sites.models import Site
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
类似于用户panchicore的回复,这是我在一个非常简单的网站上做的事情。 它提供了一些变量,并使它们在模板中可用

SITE_URL将保存类似example.com
的值 SITE_PROTOCOL将保存类似httphttps
的值 SITE_PROTOCOL_URL将保存类似http://example.comhttps://example.com
的值 SITE_PROTOCOL_RELATIVE_URL将保存类似//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 }}

快速简单,但不适合生产:

(概览)

    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,如果你使用渲染就是这种情况。

不要相信生产环境中的request.META['HTTP_HOST']:该信息来自浏览器。相反,请使用@CarlMeyer的答案

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

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

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

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

ALLOWED_HOSTS

表示这个Django站点可以提供的主机/域名的字符串列表。这是一种防止HTTP主机头攻击的安全措施,即使在许多看似安全的web服务器配置下也是可能的。

... 如果Host标头(如果启用了USE_X_FORWARDED_HOST则为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 %}

这个方法怎么样?对我有用。 它也在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)

在Django模板中,你可以这样做:

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

如果你使用“request"上下文处理器,并且正在使用Django sites框架,并且已经安装了网站的中间件(即你的设置包括这些):

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


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

... 那么你将在模板中拥有可用的request对象,并且它将包含对当前请求的Site的引用,即request.site。然后你可以在模板中检索域:

    \{\{request.site.domain}}

并附上网站名称:

    \{\{request.site.name}}

你可以使用request.build_absolute_uri()

默认情况下,它将返回一个完整的路径。

但是如果你传入一个像这样的参数:

request.build_absolute_uri('/')

这将返回域名。

我认为我们想要的是现有url标签的替代品,所以我写了一个新标签:

from django.template import Library
from django.urls     import reverse


@register.simple_tag(takes_context = True)
def fullURL(context, name, *args, **kwargs):
request = context['request']
return f'{request.scheme}://{request.get_host()}{reverse(name, args = args, kwargs = kwargs)}'

然后在你的模板中你可以这样…

{% extends "myapp/email/email_base.html" %}


{% load mytags %} {# Replace mytags with whatever the name of your custom tags calss is. #}


{% block content %}
<p>You can use <a href="{% fullURL 'signup' %}">this link</a> to get started with your account. We look forward to seeing you soon!</p>
{% endblock content %}

然后当你生成这个时,你只需要记住将request传递给context,就像这样…

from django.template.loader import render_to_string


def sendEmail(subject, to, template, **context):
html = render_to_string(f'myapp/email/{template}.html', context | {'subject': subject})
# ... and so on with the rest of my function for sending email...

正如@furins的回复中提到的,代理服务器可能存在问题。我自己在使用Apache和uWSGI时发现了这一点- request.get_hostrequest.build_absolute_uri将返回代理主机(127.0.0.1:9191…)

然而,有人已经发布了一个有用的指南来解决这个问题:

https://ubuntu.com/blog/django-behind-a-proxy-fixing-absolute-urls

虽然这是一个相对较老的答案,但它仍然与django 3.2和python 3.9相关。

为了防止这个答案在未来消失,以下是它的要点:

settings.py

# Setup support for proxy headers
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

apache.conf

<VirtualHost *:443>
...
RequestHeader set X-Forwarded-Proto 'https' env=HTTPS


ProxyPass / http://10.0.0.3/
ProxyPassReverse / http://10.0.0.3/
...
</VirtualHost>

通过这些设置,request.get_hostrequest.build_absolute_uri引用客户端请求的主机,而不是代理主机。

下面这些可以得到完整的urlurl的部分:

def myview(request):
request.build_absolute_uri()
# http://localhost:8000/admin/store/product/


request.build_absolute_uri('/')
# http://localhost:8000/


request.build_absolute_uri('/')[:-1]
# http://localhost:8000


request.scheme
# http


request.META['HTTP_HOST']
# localhost:8000


request.path
# /admin/store/product/