【问题标题】:如何在 Django 模板中获取我网站的域名?
【发布时间】:2010-11-29 21:13:32
【问题描述】:

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

【问题讨论】:

    标签: python django django-templates


    【解决方案1】:

    如果您想要实际的 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 属性(您可以找到 @987654325 @ 在站点管理面板中)。当您调用 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
    【解决方案2】:

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

    【讨论】:

    【解决方案3】:

    我认为您想要的是访问请求上下文,请参阅 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 并在将其提供给自己的浏览器之前进行修改有什么不同。
    【解决方案4】:

    作为 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.
    

    settings.py

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

    模板返回上下文实例的 url 站点是 {{ SITE_URL }}

    如果想在上下文处理器中处理子域或 SSL,您可以编写自己的程序。

    【讨论】:

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

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

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

    【讨论】:

    • build_absolute_uri 记录在案here
    【解决方案6】:

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

    (在视图中)

        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 中添加。
    • 对生产来说不够好是一个太大的警告。
    【解决方案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 包装器确保 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。
    • 如果去掉SimpleLazyObject,每个RequestContext都会调用get_current_site(),从而执行SQL查询。包装器确保仅在模板中实际使用变量时才对其进行评估。
    • 因为它是一个函数,所以除非它被使用,否则不会处理主机字符串。因此,您可以只为“site_root”分配一个函数,而不需要 SimpleLazyObject。 Django 会在使用时调用该函数。无论如何,您已经在这里使用 lambda 创建了必要的函数。
    • 啊,是的,只有 lambda 可以工作。 SimpleLazyObject 用于避免重新评估函数,因为 Site 对象已被缓存,所以实际上并不需要重新评估。
    • 导入现在是from django.contrib.sites.shortcuts import get_current_site
    【解决方案8】:

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

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

    ALLOWED_HOSTS

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

    ...如果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。我想这个历史信息与今天的大多数读者无关,但它确实澄清了这里的其他旧答案,所以我会保留它。
    【解决方案9】:

    我使用自定义模板标签。添加到例如<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/…
    • 'http://%s' 可能是https 连接的问题;在这种情况下,方案不是动态的。
    【解决方案10】:

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

    【讨论】:

      【解决方案11】:

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

      【讨论】:

        【解决方案12】:

        在 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 开发人员的绝佳资源! :)
        【解决方案13】:

        您可以使用request.build_absolute_uri()

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

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

        request.build_absolute_uri('/')
        

        这将返回域名。

        【讨论】:

          【解决方案14】:

          这种方法怎么样?为我工作。 也用于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 是有效的,而不是 @ 987654326@)。所以在开发中并不理想。
          【解决方案15】:

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

          【讨论】:

            【解决方案16】:
            from django.contrib.sites.models import Site
            if Site._meta.installed:
                site = Site.objects.get_current()
            else:
                site = RequestSite(request)
            

            【讨论】:

              【解决方案17】:

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

              【讨论】:

              • 我不认为@Erwan 注意到这取决于非标准的请求上下文处理器。
              • 我无法完成这项工作,您在哪里定义协议和域?
              • 域必须由开发者添加到上下文中。
              猜你喜欢
              • 2014-02-07
              • 2022-01-10
              • 2014-02-09
              • 1970-01-01
              • 1970-01-01
              • 2021-07-25
              • 2018-08-11
              • 2018-03-10
              • 2012-01-04
              相关资源
              最近更新 更多