如何在Django中获取完整/绝对URL(带有域)?


379

https://example.com/some/path没有Sites模块的情况下,如何在Django中获取完整/绝对URL(例如)?那真是愚蠢……我不需要查询数据库来获取URL!

我想用它reverse()


11
顺便说一句:sites模块仅在第一次需要站点名称时才访问数据库,结果被缓存在模块变量(SITE_CACHE)中,该变量将一直存在直到重新编译模块或SiteManager.clear_cache()方法被调用。请参阅:code.djangoproject.com/svn/django/tags/releases/1.3/django/...
上校Sponsz

Answers:


512

根据请求使用方便的request.build_absolute_uri()方法,向其传递相对网址,它将为您提供完整的URL。

默认情况下,request.get_full_path()返回的绝对URL ,但是您可以将相对URL作为第一个参数传递给它,以将其转换为绝对URL。


3
网址呢:localhost / home /#/ test?我只能看到localhost / home锐化后如何看到零件?
sergzach 2011年

41
#之后的所有内容都不会传递到服务器,这是仅限浏览器的功能
Dmitry Shevchenko

69
在模板(您无法提供参数)中,您可以执行以下操作:{{ request.build_absolute_uri }}{{ object.get_absolute_url }}-嘿,完整的url。
odinho-Velmont

17
如果我无权访问该怎么办?就像在Django-REST-Framework的序列化程序中一样?
miner 2014年

15
我必须使用它,{% if request.is_secure %}https://{% else %}http://{% endif %}{{ request.get_host }}{{ object.get_absolute_url }}因为{{ request.build_absolute_uri }}它后面有一个斜杠,{{ object.get_absolute_url }}并以斜杠开头,导致URL中出现双斜杠。
xtranophilist

96

如果要与它一起使用,reverse()可以执行以下操作:request.build_absolute_uri(reverse('view_name', args=(obj.pk, )))


3
感谢您的帮助。没有什么比代码本身更好。(也可以用url_name代替view_name
Anupam '18年

3
@Anupam reverse()定义为:def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
matias elgart

57

您也可以将其get_current_site用作网站应用程序(from django.contrib.sites.models import get_current_site)的一部分。它接受一个请求对象,SITE_ID如果request为,则默认为您在settings.py中配置的站点对象None。在使用站点框架的文档中阅读更多内容

例如

from django.contrib.sites.shortcuts import get_current_site
request = None
full_url = ''.join(['http://', get_current_site(request).domain, obj.get_absolute_url()])

它不像那样紧凑/简洁request.build_absolute_url(),但是当请求对象不可用并且您具有默认站点URL时,它可以使用。


4
我相信我的问题特别是说“没有网站模块”。这会打击数据库吗?
mpen 2012年

1
已将Sites模块编写为使用模块级缓存来缓存Site对象(即,您不需要缓存框架),因此,数据库仅应在Web进程第一次检索Site时被命中。如果你没有django.contrib.sites在你的INSTALLED_APPS,也不会打DB可言,并提供基于Request对象的信息(见get_current_site
Darb

1
好了,您可以拥有+1,但build_absolute_uri看起来仍然是更简单,更干净的解决方案。
mpen 2012年

1
如果您试图在信号中生成URL来发送电子邮件,那么这是一个完美的答案。
克里斯,

2
如果使用https,则不起作用。是的,您可以添加s,但是您是否在本地使用https开发?并且您始终知道,如果您有https,但有时没有...?
tjati 2014年

55

如果无法访问,request则无法get_current_site(request)按此处某些解决方案中的建议使用。您可以使用本机Sites框架的组合来get_absolute_url代替。在管理员中设置至少一个Site,确保您的模型具有get_absolute_url()方法,然后:

>>> from django.contrib.sites.models import Site
>>> domain = Site.objects.get_current().domain
>>> obj = MyModel.objects.get(id=3)
>>> path = obj.get_absolute_url()

>>> url = 'http://{domain}{path}'.format(domain=domain, path=path)
>>> print(url)
'http://example.com/mymodel/objects/3/'

https://docs.djangoproject.com/zh-CN/dev/ref/contrib/sites/#getting-the-current-domain-for-full-urls


7
当您无权访问HttpRequest对象时,这真的很方便。例如在任务,信号等中
Arsham 2014年

6
使用此之前,你应该启用站点框架docs.djangoproject.com/en/dev/ref/contrib/sites/...
madzohan

还要将example.com更改为:Site.objects.all()[0]返回'example.com',并且ID = 1(在settings.py中指定)。只需执行Site.objects.create(name ='production',domain ='prodsite.com')并在settings.py中设置SITE_ID = 2。现在Site.objects.get_current()。domain返回'prodsite.com'。
gek

您可以设置requestNone或致电get_current_site(None)
Bobort

20

如果您不想访问数据库,则可以通过设置来完成。然后,使用上下文处理器将其添加到每个模板中:

# settings.py (Django < 1.9)
...
BASE_URL = 'http://example.com'
TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    'myapp.context_processors.extra_context',
)
# settings.py (Django >= 1.9)
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                # Additional
                'myapp.context_processors.extra_context',
            ],
        },
    },
]

# myapp/context_processors.py
from django.conf import settings

def extra_context(request):
    return {'base_url': settings.BASE_URL}

# my_template.html
<p>Base url is {{ base_url }}.</p>

17

在您看来,只需执行以下操作:

base_url =  "{0}://{1}{2}".format(request.scheme, request.get_host(), request.path)

14

django-fullurl

如果你想做到这一点在Django模板,我已经发布了一个很小的安装包的PyPI django-fullurl让你更换urlstatic用模板标签fullurlfullstatic,如下所示:

{% load fullurl %}

Absolute URL is: {% fullurl "foo:bar" %}

Another absolute URL is: {% fullstatic "kitten.jpg" %}

这些徽章有望自动保持最新状态:

聚酰亚胺 特拉维斯CI

在视图中,您当然可以request.build_absolute_uri代替使用。


遗憾的是,这不适用于2.0。可能需要提高PR。
史蒂文·丘奇

@StevenChurch它应该工作。我尚未将Django 2.0标记为受支持,但是现有版本应该可以使用。
Flimm

对于我的需要,我通过从Heroku传递ENV进行故障回复来解决此问题。我的问题是使URL传递到电子邮件模板。我不记得这个问题了,但是由于Django的改变而无法解决。
史蒂文·丘奇

@StevenChurch我认为创建电子邮件时的问题是没有request对象可以获取域名。在这种情况下,您应该改用sites框架,该框架从数据库获取域名。请参见django-absoluteuri,在此PyPI软件包的自述文件的“另请参见”部分中提到。
Flimm '18

8

要创建从模板到另一个页面的完整链接,可以使用以下方法:

{{ request.META.HTTP_HOST }}{% url 'views.my_view' my_arg %}

request.META.HTTP_HOST提供主机名,而url提供相对名。然后,模板引擎将它们串联成一个完整的URL。


2
答案是缺少协议(http在这种情况下)和://URL的一部分,因此它不会提供完整的url
user272735

2
请求对象上有一个主机。不要直接检查元数据:docs.djangoproject.com/en/1.8/ref/request-response/…–
Kit Sunde

8

还有另一种方式。您可以build_absolute_uri()在其中使用view.py并将其传递给模板。

view.py

def index(request):
    baseurl = request.build_absolute_uri()
    return render_to_response('your-template.html', { 'baseurl': baseurl })

your-template.html

{{ baseurl }}

HttpRequest.build_absolute_uri(request)等于request.build_absolute_uri()不是吗?
mpen


7

尝试以下代码:

{{ request.scheme }}://{{ request.META.HTTP_HOST }}

那只会给域没有路径和查询字符串,不是吗?
mpen

6

这在我的模板中对我有用:

{{ request.scheme }}:{{ request.META.HTTP_HOST }}{% url  'equipos:marca_filter' %}

我需要完整的网址才能将其传递给js提取函数。希望对您有所帮助。


5

我知道这是一个老问题。但是我认为人们仍然会遇到很多问题。

有一些库可以补充默认的Django功能。我已经尝试了一些。当反向引用绝对URL时,我喜欢以下库:

https://github.com/fusionbox/django-absoluteuri

我喜欢的另一个参数是因为您可以轻松地将域,协议和路径组合在一起:

https://github.com/RRMoelker/django-full-url

该库使您可以简单地在模板中编写所需内容,例如:

{{url_parts.domain}}

4

如果您使用的是django REST框架,则可以使用中的反向功能rest_framework.reverse。它具有与相同的行为django.core.urlresolvers.reverse,除了它使用请求参数来构建完整的URL。

from rest_framework.reverse import reverse

# returns the full url
url = reverse('view_name', args=(obj.pk,), request=request)

# returns only the relative url
url = reverse('view_name', args=(obj.pk,))

编辑仅提及REST框架中的可用性


使用时出现错误request=request。似乎也未在此处记录该
Ryan Amos

我忘了提到这仅在您使用REST框架时可用。很好,我已经更新了答案。
JohnG '16

是的谢谢你-这个工程就像一个魅力Django的REST框架
Apoorv Kansal

1

我知道了:

wsgiref.util.request_uri(request.META)

使用架构,主机,端口路径和查询获取完整的uri。


0

还提供ABSOLUTE_URL_OVERRIDES作为设置

https://docs.djangoproject.com/zh-CN/2.1/ref/settings/#absolute-url-overrides

但这会覆盖get_absolute_url(),这可能是不希望的。

我认为不是更好地为此安装站点框架或执行此处提及的依赖请求对象的其他一些工作,我认为更好的解决方案是将其放置在models.py中

在settings.py中定义BASE_URL,然后将其导入到models.py中,并创建一个抽象类(或将其添加到您正在使用的类中),该类定义get_truly_absolute_url()。它可能很简单:

def get_truly_absolute_url(self):
    return BASE_URL + self.get_absolute_url()

对其进行子类化,现在您可以在任何地方使用它。


0

如其他答案所述,request.build_absolute_uri()如果您可以访问requestsites只要不同的URL指向不同的数据库框架就很好。

但是,我的用例略有不同。我的登台服务器和生产服务器访问相同的数据库,但是get_current_site都返回site了数据库中的第一个数据库。若要解决此问题,您必须使用某种环境变量。您可以为不同的服务器和不同的settings.py使用1)环境变量(类似os.environ.get('SITE_URL', 'localhost:8000'))或2)different 。SITE_ID

希望有人会发现这个有用!


0

我遇到了这个线程,因为我正在寻找一个成功页面的绝对URI。request.build_absolute_uri()给了我当前视图的URI,但是为了获得成功视图的URI,我使用了以下方法...。

request.build_absolute_uri(reverse('success_view_name'))



-5

您还可以使用:

import socket
socket.gethostname()

这对我来说很好

我不完全确定它是如何工作的。我相信这是一个较低的级别,它将返回您的服务器主机名,该主机名可能与用户访问页面所使用的主机名不同。


是的..您指出了问题所在。主机名不必与域名相同。
mpen 2016年

这解决了一个非常不同的问题。考虑具有多个网站的共享托管服务器-使用上面的代码,所有生成URL的站点都将具有指向主机的所有此类URL,这很可能不是任何正在运行的网站。
tbm

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.