How can I get the full/absolute URL (e.g. https://example.com/some/path
) in Django without the Sites module? That's just silly... I shouldn't need to query my DB to snag the URL!
I want to use it with reverse()
.
How can I get the full/absolute URL (e.g. https://example.com/some/path
) in Django without the Sites module? That's just silly... I shouldn't need to query my DB to snag the URL!
I want to use it with reverse()
.
Use handy request.build_absolute_uri() method on request, pass it the relative url and it'll give you full one.
By default, the absolute URL for request.get_full_path()
is returned, but you can pass it a relative URL as the first argument to convert it to an absolute URL.
If you can't get access to request
then you can't use get_current_site(request)
as recommended in some solutions here. You can use a combination of the native Sites framework and get_absolute_url
instead. Set up at least one Site in the admin, make sure your model has a get_absolute_url() method, then:
>>> 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/en/dev/ref/contrib/sites/#getting-the-current-domain-for-full-urls
You can also use get_current_site
as part of the sites app (from django.contrib.sites.models import get_current_site
). It takes a request object, and defaults to the site object you have configured with SITE_ID
in settings.py if request is None
. Read more in documentation for using the sites framework
e.g.
from django.contrib.sites.shortcuts import get_current_site
request = None
full_url = ''.join(['http://', get_current_site(request).domain, obj.get_absolute_url()])
It isn't as compact/neat as request.build_absolute_url()
, but it is usable when request objects are unavailable, and you have a default site url.
If you don't want to hit the database, you could do it with a setting. Then, use a context processor to add it to every template:
# 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>
django-fullurl
If you're trying to do this in a Django template, I've released a tiny PyPI package django-fullurl
to let you replace url
and static
template tags with fullurl
and fullstatic
, like this:
{% load fullurl %}
Absolute URL is: {% fullurl "foo:bar" %}
Another absolute URL is: {% fullstatic "kitten.jpg" %}
These badges should hopefully stay up-to-date automatically:
In a view, you can of course use request.build_absolute_uri
instead.
I know this is an old question. But I think people still run into this a lot.
There are a couple of libraries out there that supplement the default Django functionality. I have tried a few. I like the following library when reverse referencing absolute urls:
https://github.com/fusionbox/django-absoluteuri
Another one I like because you can easily put together a domain, protocol and path is:
https://github.com/RRMoelker/django-full-url
This library allows you to simply write what you want in your template, e.g.:
{{url_parts.domain}}
If you're using django REST framework, you can use the reverse function from rest_framework.reverse
. This has the same behavior as django.core.urlresolvers.reverse
, except that it uses a request parameter to build a full 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,))
Edited to mention availability only in REST framework
If anyone is interested in fetching the absolute reverse url with parameters in a template , the cleanest way is to create your own absolute version of the {% url %}
template tag by extending and using existing default code.
Here is my code:
from django import template
from django.template.defaulttags import URLNode, url
register = template.Library()
class AbsURLNode(URLNode):
def __init__(self, view_name, args, kwargs, asvar):
super().__init__(view_name, args, kwargs, asvar)
def render(self, context):
url = super().render(context)
request = context['request']
return request.build_absolute_uri(url)
@register.tag
def abs_url(parser, token):
urlNode = url(parser, token)
return AbsURLNode( urlNode.view_name, urlNode.args, urlNode.kwargs, urlNode.asvar )
Usage in templates:
{% load wherever_your_stored_this_tag_file %}
{% abs_url 'view_name' parameter %}
will render(example):
http://example.com/view_name/parameter/
instead of
/view_name/parameter/
There is also ABSOLUTE_URL_OVERRIDES available as a setting
https://docs.djangoproject.com/en/2.1/ref/settings/#absolute-url-overrides
But that overrides get_absolute_url(), which may not be desirable.
Instead of installing sites framework just for this or doing some of the other stuff mentioned here that relies on request object, I think the better solution is to place this in models.py
Define BASE_URL in settings.py, then import it into models.py and make an abstract class (or add it to one you're already using) that defines get_truly_absolute_url(). It could be as simple as:
def get_truly_absolute_url(self):
return BASE_URL + self.get_absolute_url()
Subclass it and now you can use it everywhere.
As mentioned in other answers, request.build_absolute_uri()
is perfect if you have access to request
, and sites
framework is great as long as different URLs point to different databases.
However, my use case was slightly different. My staging server and the production server access the same database, but get_current_site
both returned the first site
in the database. To resolve this, you have to use some kind of environment variable. You can either use 1) an environment variable (something like os.environ.get('SITE_URL', 'localhost:8000')
) or 2) different SITE_ID
s for different servers AND different settings.py.
Hopefully someone will find this useful!
class WalletViewSet(mixins.ListModelMixin, GenericViewSet):
serializer_class = WalletSerializers
pagination_class = CustomPaginationInvestment
def get_queryset(self):
######################################################
print(self.request.build_absolute_uri())
#####################################################
wallet, created = Wallet.objects.get_or_create(owner=self.request.user)
return Wallet.objects.filter(id=wallet.id)
You get output like this
http://localhost:8000/v1/wallet
HTTP GET /v1/wallet 200 [0.03, 127.0.0.1:41608]