ChatGPT解决这个技术问题 Extra ChatGPT

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

如何从 Django 模板中获取当前站点的域名?我试过查看标签和过滤器,但那里什么也没有。


S
Southpaw Hare

如果您想要实际的 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 对象,则必须自己将其放入模板上下文中。如果您到处都在使用它,则可以将其打包在模板上下文处理器中。


为了澄清与我遇到相同问题的人:检查您的 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
e
eykanal

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


请注意,此答案与 Daniel Roseman 方法存在相同的问题(它可以被欺骗),但是当通过 HTTP 代理或负载平衡器到达主机时它肯定会更完整,因为它考虑了 HTTP_X_FORWARDED_HOST HTTP 标头。
用法:“//{{ request.get_host }}/anything/else/you/want”...请务必填写您的 ALLOWED_HOSTS 设置(请参阅 docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts)。
@Seth 更好地使用 request.build_absolute_uri (docs.djangoproject.com/en/dev/ref/request-response/…)
我需要动态地为我的应用程序构建主机名,因为我不必使用三元语句在本地开发版本和生产的字符串之间切换,这两者都需要测试支付网关集成系统..使用 request.get_host( ) 在视图中并添加正斜杠到返回的字符串做了工作
p
phsiao

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


request.META['HTTP_HOST'] 为您提供域。在模板中它将是 {{ request.META.HTTP_HOST }}
小心使用请求元数据。它来自浏览器,可以被欺骗。一般来说,您可能希望采用@CarlMeyer 下面的建议。
就我而言,这没有安全漏洞。
我想,因为 Django 1.5 设置了允许的主机,所以可以安全使用。 docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
有人可以详细说明“安全漏洞”是什么吗?如果用户欺骗了 Host: 标头并在页面上的某处获得了带有欺骗域的响应,这会如何造成安全漏洞?我看不出这与用户获取生成的 HTML 并在将其提供给自己的浏览器之前修改自己有什么不同。
p
panchicore

作为 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,您可以编写自己的程序。


我尝试了这个解决方案,但是如果您有多个子域用于同一个应用程序,这是不切实际的,我发现 danbruegge 的答案非常有用
在 settings.py 你必须在 context_processors>OPTIONS>TEMPLATES 中引入你的上下文处理器
m
misterte

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

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

build_absolute_uri 已记录在案 here
E
Edward Newell

快速简单,但不利于生产:

(在视图中)

    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 中添加。
对生产来说不够好是一个太大的警告。
v
vdboor

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

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 查询。包装器确保仅在模板中实际使用变量时才对其进行评估。
由于它是一个函数,除非它被使用,否则不会处理主机字符串。因此,您可以只为“site_root”分配一个函数,而不需要 SimpleLazyObject。 Django 会在使用时调用该函数。无论如何,您已经在这里使用 lambda 创建了必要的函数。
啊,是的,只有 lambda 可以工作。 SimpleLazyObject 用于避免重新评估函数,因为 Site 对象已缓存,因此实际上并不需要重新评估。
导入现在是 from django.contrib.sites.shortcuts import get_current_site
S
S. Kirby

当与 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 应该得到 httphttps,具体取决于您使用的是什么:

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

到 Django 1.8 的链接已失效... this 现在是否等价?
@JamesBellaby 啊,看起来我链接到的页面现在只存在于旧文档版本中。我将更新指向 this 的链接。我想这个历史信息与今天的大多数读者无关,但它确实澄清了这里的其他旧答案,所以我会保留它。
D
Dennis Golomazov

我使用自定义模板标签。添加到例如 <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 我不知道。 get_current 是记录在案的方法:docs.djangoproject.com/en/dev/ref/contrib/sites/…
https 连接的情况下,'http://%s' 可能会出现问题;在这种情况下,方案不是动态的。
H
Harry Moreno

如果您使用 "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}}

J
Julián Landerreche

类似于用户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
    }

设置.py

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

SITE_URL = 'example.com'

然后,在您的模板上,将它们用作 {{ SITE_URL }}{{ SITE_PROTOCOL }}{{ SITE_PROTOCOL_URL }}{{ SITE_PROTOCOL_RELATIVE_URL }}


D
Dos

在 Django 模板中,您可以执行以下操作:

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

这对我有用,谢谢。我必须在 TEMPLATES、context_processors:django.template.context_processors.request 中启用请求,还有 [this how-to help](simpleisbetterthancomplex.com/tips/2016/07/20/…)
同意,Vitor Freitas 博客是 Django 开发人员的绝佳资源! :)
K
Kos

您可以使用 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 无效),它将无法工作。因此,在仍处于开发阶段时,它并不理想。
A
ArtOfWarfare

我想我们想要的是现有 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...

t
typonaut

正如@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 相关。

以防万一答案在未来消失,这里是它的要点:

设置.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 引用客户端请求的主机而不是代理主机。


M
Muneeb Ahmad
from django.contrib.sites.models import Site
if Site._meta.installed:
    site = Site.objects.get_current()
else:
    site = RequestSite(request)

K
Kai - Kazuya Ito

下面这些可以获得完整的 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/

E
Erwan

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


我不认为@Erwan 注意到这取决于非标准请求上下文处理器。
我无法完成这项工作,您在哪里定义协议和域?
域必须由开发人员添加到上下文中。