如何从 Django 模板中获取当前站点的域名?我试过查看标签和过滤器,但那里什么也没有。
如果您想要实际的 HTTP Host 标头,请参阅 Daniel Roseman 对 @Phsiao 的回答的评论。另一种选择是,如果您使用 contrib.sites framework,您可以在数据库中为站点设置规范域名(将请求域映射到具有正确 SITE_ID 的设置文件是您必须通过网络服务器自己完成的事情设置)。在这种情况下,您正在寻找:
from django.contrib.sites.models import Site
current_site = Site.objects.get_current()
current_site.domain
如果要使用 current_site 对象,则必须自己将其放入模板上下文中。如果您到处都在使用它,则可以将其打包在模板上下文处理器中。
我发现了 {{ request.get_host }}
方法。
HTTP_X_FORWARDED_HOST
HTTP 标头。
request.build_absolute_uri
(docs.djangoproject.com/en/dev/ref/request-response/…)
我认为您想要的是访问请求上下文,请参阅 RequestContext。
request.META['HTTP_HOST']
为您提供域。在模板中它将是 {{ request.META.HTTP_HOST }}
。
Host:
标头并在页面上的某处获得了带有欺骗域的响应,这会如何造成安全漏洞?我看不出这与用户获取生成的 HTML 并在将其提供给自己的浏览器之前修改自己有什么不同。
作为 Carl Meyer 的补充,您可以制作这样的上下文处理器:
module.context_processors.py
from django.conf import settings
def site(request):
return {'SITE_URL': settings.SITE_URL}
本地设置.py
SITE_URL = 'http://google.com' # this will reduce the Sites framework db call.
设置.py
TEMPLATE_CONTEXT_PROCESSORS = (
...
"module.context_processors.site",
....
)
模板返回上下文实例 url 站点是 {{ SITE_URL }}
如果想在上下文处理器中处理子域或 SSL,您可以编写自己的程序。
我知道这个问题很老,但我偶然发现它正在寻找一种获取当前域的pythonic方法。
def myview(request):
domain = request.build_absolute_uri('/')[:-1]
# that will build the complete domain: http://foobar.com
build_absolute_uri
已记录在案 here。
快速简单,但不利于生产:
(在视图中)
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 中可用。
request.scheme
在 Django 1.7 中添加。
我使用的上下文处理器的变体是:
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)),
}
SimpleLazyObject
,因为如果无论如何都没有访问“站点”,则不会调用 lambda。
SimpleLazyObject
,则每个 RequestContext
都将调用 get_current_site()
,从而执行 SQL 查询。包装器确保仅在模板中实际使用变量时才对其进行评估。
SimpleLazyObject
用于避免重新评估函数,因为 Site
对象已缓存,因此实际上并不需要重新评估。
from django.contrib.sites.shortcuts import get_current_site
当与 ALLOWED_HOSTS
设置(在 Django 1.4.4 中添加)一起使用时,{{ request.get_host }}
应防止 HTTP 主机头攻击。
请注意,{{ request.META.HTTP_HOST }}
没有相同的保护。请参阅 docs:
ALLOWED_HOSTS 表示此 Django 站点可以服务的主机/域名的字符串列表。这是一种防止 HTTP 主机头攻击的安全措施,即使在许多看似安全的 Web 服务器配置下也是可能的。 ... 如果 Host 标头(或 X-Forwarded-Host,如果启用了 USE_X_FORWARDED_HOST)与此列表中的任何值都不匹配,则 django.http.HttpRequest.get_host() 方法将引发 SuspiciousOperation。 ...此验证仅适用于 get_host();如果您的代码直接从 request.META 访问 Host 标头,则您将绕过此安全保护。
至于在模板中使用 request
,模板渲染函数调用有 changed in 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 %}
我使用自定义模板标签。添加到例如 <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 %}
get_current
是记录在案的方法:docs.djangoproject.com/en/dev/ref/contrib/sites/…
https
连接的情况下,'http://%s'
可能会出现问题;在这种情况下,方案不是动态的。
如果您使用 "request" context processor,并且正在使用 Django sites framework,并且安装了 Site middleware(即您的设置包括这些):
INSTALLED_APPS = [
...
"django.contrib.sites",
...
]
MIDDLEWARE = [
...
"django.contrib.sites.middleware.CurrentSiteMiddleware",
...
]
... 那么您将在模板中获得 request
对象,并且它将包含对当前 Site
的引用作为 request.site
的请求。然后,您可以使用以下方法在模板中检索域:
{{request.site.domain}}
和网站名称:
{{request.site.name}}
类似于用户panchicore的回复,这是我在一个很简单的网站上做的。它提供了一些变量并使它们在模板上可用。
SITE_URL
会保存类似 example.com
的值 SITE_PROTOCOL
会保存类似 http
或 https
的值 SITE_PROTOCOL_URL
会保存类似 http://example.com
或 https://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
}
设置.py
TEMPLATE_CONTEXT_PROCESSORS = (
...
"module.context_processors.site",
....
)
SITE_URL = 'example.com'
然后,在您的模板上,将它们用作 {{ SITE_URL }}
、{{ SITE_PROTOCOL }}
、{{ SITE_PROTOCOL_URL }}
和 {{ SITE_PROTOCOL_RELATIVE_URL }}
在 Django 模板中,您可以执行以下操作:
<a href="{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ request.path }}?{{ request.GET.urlencode }}" >link</a>
django.template.context_processors.request
中启用请求,还有 [this how-to help](simpleisbetterthancomplex.com/tips/2016/07/20/…)
您可以使用 request.build_absolute_uri()
默认情况下,它将返回完整路径。
但是如果你传入这样的参数:
request.build_absolute_uri('/')
这将返回域名。
这种方法怎么样?为我工作。它也用于 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
无效),它将无法工作。因此,在仍处于开发阶段时,它并不理想。
我想我们想要的是现有 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_host
或 request.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 相关。
以防万一答案在未来消失,这里是它的要点:
设置.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_host
和 request.build_absolute_uri
引用客户端请求的主机而不是代理主机。
from django.contrib.sites.models import Site
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
下面这些可以获得完整的 url 和部分 url:
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/
您可以在模板中使用 {{ protocol }}://{{ domain }}
来获取您的域名。
不定期副业成功案例分享
SITE_ID
设置是否等于站点应用程序中当前站点的id
属性(您可以在站点管理面板中找到id
)。当您调用get_current
时,Django 会使用您的SITE_ID
并从数据库中返回具有该 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