498

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().

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 15
    Just as an aside: The sites module only hits the DB the first time it needs the site name, the result is cached in a module variable (SITE_CACHE) that will stick around until re-compilation of the module or the SiteManager.clear_cache() method is called. See: http://code.djangoproject.com/svn/django/tags/releases/1.3/django/contrib/sites/models.py – Colonel Sponsz May 01 '11 at 11:27

30 Answers30

678

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.

>>> request.build_absolute_uri()
'https://example.com/music/bands/the_beatles/?print=true'
>>> request.build_absolute_uri('/bands/?print=true')
'https://example.com/bands/?print=true'
Flimm
  • 136,138
  • 45
  • 251
  • 267
Dmitry Shevchenko
  • 31,814
  • 10
  • 56
  • 62
  • +1 This function uses the same data that are in my answer. And is definitely a more convenient option. – Kugel Feb 27 '10 at 12:17
  • 3
    What about the url: http://localhost/home/#/test ? I can see only http://localhost/home/. How can I see the part after *sharp*? – sergzach Sep 18 '11 at 19:23
  • 58
    everything after # is not passed to the server, it's browser-only feature – Dmitry Shevchenko Sep 19 '11 at 09:01
  • 81
    In a template (where you can't give parameters) you can just do this: `{{ request.build_absolute_uri }}{{ object.get_absolute_url }}` - and heyho, full url. – odinho - Velmont Apr 02 '13 at 13:51
  • 2
    This a great answer for most places in django where a request object is available but Darb's answer http://stackoverflow.com/questions/2345708/how-can-i-get-the-full-absolute-url-with-domain-in-django/8817935#8817935 works great for applications such as signal processing to generate links in emails and such. When you dont have a request object handy. – Chris Jul 08 '13 at 19:21
  • 28
    And what if I don't have access to request? Like in Django-REST-Framework's Serializers? – minder Nov 20 '14 at 21:15
  • 1
    IIRC you can get to request from serializer `self.context`, or at least you can put it there in your view. – Dmitry Shevchenko Nov 21 '14 at 00:35
  • 19
    I had to use `{% if request.is_secure %}https://{% else %}http://{% endif %}{{ request.get_host }}{{ object.get_absolute_url }}` because `{{ request.build_absolute_uri }}` had a trailing slash and `{{ object.get_absolute_url }}` started with a slash resulting in double slashes in the URL. – xtranophilist Jul 27 '15 at 18:23
  • 2
    If get_absolute_url always has a leading slash I feel it's cleaner to just slice it off like so: {{request.build_absolute_uri}}{{object.get_absolute_url|slice:"1:"}} – Norgg Jan 26 '16 at 13:43
  • 2
    @minder To get the request from Django-rest-framework, in your serializer function call: request = self.context.get('request', None) – Scott Jul 28 '16 at 20:25
  • Passing a string into the function adds the string to the absolute path, while prepending the string with `/` returns the absolute path with the relative path replaced with just the string. Example `request.get_full_path()` returns `/foo/bar/` `request.build_full_uri('baz')` returns `http://domain.com/foo/bar/baz` `request.build_full_uri('/baz')` returns `http://domain.com/baz` – asgaines Aug 21 '16 at 04:33
  • 2
    I get the following error: `ipdb> HttpRequest.build_absolute_uri(la_url) *** AttributeError: 'str' object has no attribute 'get_full_path'` – tread Jan 04 '18 at 10:24
  • @Velmont `build_absolute_uri` returns *current url* when no arguments passed. `{{ request.build_absolute_uri }}{{ object.get_absolute_url }}` might work only on the home page, owing to `//` being interpreted as `/`. – x-yuri May 22 '19 at 12:36
  • 1
    how to do this, if I don't have a request object. Just like using a serializer in Channels. In Views, we can pass the request object to serializer. But in Channels there is no request object, instead, we have scope. – Abhijith Konnayil Apr 23 '21 at 03:55
  • @AbhijithK At that point, you probably want to find the current domain using the [Site object](https://docs.djangoproject.com/en/4.0/ref/contrib/sites/), and build a URL that way. – Flimm Jan 18 '22 at 09:29
137

If you want to use it with reverse() you can do this : request.build_absolute_uri(reverse('view_name', args=(obj.pk, )))

ébewè
  • 2,068
  • 2
  • 14
  • 11
  • 3
    Thanks for the helpful answer. Nothing better than code itself. (also, you probably meant `url_name` instead of `view_name`) – Anupam May 01 '18 at 10:55
  • 3
    @Anupam reverse() is defined as: `def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)` – matias elgart Jan 31 '19 at 03:26
  • @ébewè how would one use this in a template? – PowerAktar Jan 07 '21 at 18:54
  • If anyone is interested, you can omit the `args=(obj.pk, )` portion if you do not require any parameters. Helped me! – Ricky Sep 02 '21 at 23:41
  • 1
    This works well for building full url when you have the request object(e.g on a view) and the url is for get request so that redirect http -> https is still fine. This will not work when the request object is not available. For example, to construct the url in scheduled task or asynced task. And the url to hanle post request and the frontend is using https while the django handles request via a proxy – Dat TT Jan 12 '23 at 15:00
87

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 = f'https://{domain}{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

shacker
  • 14,712
  • 8
  • 89
  • 89
  • 11
    This is really handy when you don't have access to HttpRequest object. e.g. in tasks, signals etc. – Arsham Nov 11 '14 at 10:04
  • 6
    before using this you should enable sites framework https://docs.djangoproject.com/en/dev/ref/contrib/sites/#enabling-the-sites-framework – madzohan Jul 01 '15 at 15:12
  • To change example.com to something also: Site.objects.all()[0] returns 'example.com' and has id=1, which specified in settings.py. Just do Site.objects.create(name='production', domain='prodsite.com') and set SITE_ID=2 in settings.py. Now Site.objects.get_current().domain returns 'prodsite.com'. – gek Nov 07 '19 at 14:10
  • You can set `request` to `None` or call `get_current_site(None)`. – Bobort Mar 24 '20 at 15:05
  • 1
    My development domain is at "127.0.0.1:8000" and the production domain is 'paidfor.pythonanywhere.com'. I want my Django to find own its own which domain is it running on. – Shalom Alexander Jun 07 '21 at 07:51
  • As @Shalom Alexander points out, this is not flexible for development and production. And if the project is worked by one developer , it is fine but I am sure there is a chance you will fail to update it and it breaks in one environment or the other. – Dat TT Jan 12 '23 at 15:08
  • 1
    @ShalomAlexander How is this not flexible between dev and prod? `get_current()` takes care of that abstraction for you. – shacker Jan 17 '23 at 01:07
76

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.

avatarez
  • 35
  • 6
Darb
  • 1,463
  • 11
  • 10
  • 6
    I believe my question specifically said "without the Sites module". Does this hit the DB? – mpen Jan 11 '12 at 19:41
  • 3
    The Sites module has been written to cache Site objects using module level caching (i.e. you don't need the cache framework), so the DB should only get hit the first time a Site is retrieved by a web process. If you don't have `django.contrib.sites` in your `INSTALLED_APPS`, it won't hit the DB at all, and provide information based on the Request object (see [get_current_site](https://github.com/django/django/blob/1.3.X/django/contrib/sites/models.py#L86)) – Darb Jan 31 '12 at 10:04
  • 1
    Well then you can has a +1, but `build_absolute_uri` still looks like the easier and cleaner solution. – mpen Jan 31 '12 at 19:21
  • Would be nice if there was a Site.build_absolute_uri shortcut, or if build_absolute_uri could take None as the request object... – Darb Feb 01 '12 at 09:07
  • to avoid TypeErros you should wrap `get_current_site(request)` with a call to `str`, that way you obtain a string instead of a Site object – glarrain Dec 07 '12 at 16:03
  • 1
    This is a perfect answer if you are trying to generate URLs in signals to dispatch emails from. – Chris Jul 08 '13 at 19:20
  • 3
    Does not work, if you use https. Yeah, you could add the s, but do you develop with https locally? and do you always know, if you have https but not sometimes...? – tjati May 05 '14 at 12:30
  • 1
    get_current_site() depends on access to the request object, though you say "usable when request objects are unavailable". How can we do this without access to request? – shacker Jul 25 '14 at 18:26
  • 1
    @shacker it's in the example there, you can pass in request = None – Darb Jul 27 '14 at 13:49
  • for local testing purposes, does that mean I'd need to add a Site for `localhost` and point the respective `SITE_ID` to it? – wasabigeek Feb 23 '17 at 15:23
  • 1
    I use `request.scheme + '://'` instead of `http://` because it's HTTPS in production and HTTP locally. – Bartleby Nov 15 '19 at 00:41
26

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>
Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
seddonym
  • 16,304
  • 6
  • 66
  • 71
  • Good stuff. This combined with a .env better satisfies my use case. – alexcs Dec 17 '20 at 12:35
  • Yes, this is the best solution with environment variable, especially for large team and multi environment(dev, staging, uat, production,etc) and de-coupled backend and frontend. The down side is to maintain the .env file for each environments. – Dat TT Jan 12 '23 at 15:18
26

In your view, just do this:

base_url =  "{0}://{1}{2}".format(request.scheme, request.get_host(), request.path)
levi
  • 22,001
  • 7
  • 73
  • 74
20

This worked for me in my template:

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

I needed the full url to pass it to a js fetch function. I hope this help you.

VertigoRay
  • 5,935
  • 6
  • 39
  • 48
18

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:

PyPI

In a view, you can of course use request.build_absolute_uri instead.

Flimm
  • 136,138
  • 45
  • 251
  • 267
  • Shame this does not work with 2.0. Might need to push up a PR. – Kyias Aug 06 '18 at 15:44
  • @StevenChurch It should work. I haven't marked Django 2.0 as supported yet, but the existing version should work. – Flimm Aug 06 '18 at 16:53
  • For my needs I got round this by passing a ENV from Heroku for failback. My issue is getting the URL to pass through to email templates. I can't remember the problem but it did not work due to a Django change. – Kyias Aug 09 '18 at 11:17
  • 1
    @StevenChurch I think the issue when creating emails is that there is no `request` object to get the domain name from. In that case, you should use the `sites` framework instead, which gets the domain name from the database. See `django-absoluteuri`, mentioned in the "see also" section of the README of this PyPI package. – Flimm Aug 14 '18 at 12:38
13

Yet another way. You could use build_absolute_uri() in your view.py and pass it to the template.

view.py

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

your-template.html

{{ baseurl }}
Sven Rojek
  • 5,476
  • 2
  • 35
  • 57
  • `HttpRequest.build_absolute_uri(request)` is equivalent to `request.build_absolute_uri()` isn't it? – mpen Jan 16 '15 at 21:54
11

Examine Request.META dictionary that comes in. I think it has server name and server port.

Kugel
  • 19,354
  • 16
  • 71
  • 103
10

To create a complete link to another page from a template, you can use this:

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

request.META.HTTP_HOST gives the host name, and url gives the relative name. The template engine then concatenates them into a complete url.

Doug Bradshaw
  • 1,452
  • 1
  • 16
  • 20
  • 2
    The answer is missing the protocol (`http` in this context) and `://` part of the URL, so it won't provide _a complete url_. – user272735 Mar 08 '15 at 11:48
  • 2
    The request object has a host on it. Don't examine meta directly: https://docs.djangoproject.com/en/1.8/ref/request-response/#django.http.HttpRequest.get_host – Kit Sunde Oct 02 '15 at 12:56
10

Try the following code:

{{ request.scheme }}://{{ request.META.HTTP_HOST }}
Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
mark
  • 851
  • 1
  • 8
  • 5
7

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

JohnG
  • 653
  • 8
  • 14
  • I get an error using `request=request`. It also doesn't seem like request is documented here https://docs.djangoproject.com/en/1.9/ref/urlresolvers/#reverse – Ryan Amos May 22 '16 at 06:16
  • I forgot to mention this is only available if you're using the REST framework. Good catch, I've updated my answer. – JohnG May 22 '16 at 18:31
  • Yes thank you - this works like a charm with **django REST framework** – Apoorv Kansal Dec 17 '16 at 00:21
6

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}}
johniak20
  • 71
  • 1
  • 2
5

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/
PowerAktar
  • 2,341
  • 1
  • 21
  • 17
  • Thanks for this. Getting request object from URLNode instance `request = context['request']` was it – Anupam Sep 07 '22 at 11:01
3

I got it:

wsgiref.util.request_uri(request.META)

Get the full uri with schema, host, port path and query.

wonder
  • 521
  • 4
  • 11
3

You can either pass request reverse('view-name', request=request) or enclose reverse() with build_absolute_uri request.build_absolute_uri(reverse('view-name'))

Fahmi Eshaq
  • 318
  • 3
  • 10
3

Not for absolute url but I was looking just to get host. If you want to get host in your view.py you can do

def my_view(request):
   host = f"{ request.scheme }://{ request.META.get('HTTP_HOST') }"
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
sultanmyrza
  • 4,551
  • 1
  • 30
  • 24
2

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_IDs for different servers AND different settings.py.

Hopefully someone will find this useful!

Bartleby
  • 1,144
  • 1
  • 13
  • 14
2

I came across this thread because I was looking to build an absolute URI for a success page. request.build_absolute_uri() gave me a URI for my current view but to get the URI for my success view I used the following....

request.build_absolute_uri(reverse('success_view_name'))

Soundtemple
  • 641
  • 2
  • 9
  • 8
2

While working on a project I came to know to get the full/absolute URL in Django.

If your URL looks like this in the address bar:

https://stackoverflow.com/questions/2345708

And if you want to show the above URL to your template.

  1. {{ request.path }} #Without GET parameters.
  2. {{ request.get_full_path }} #with GET parameters

For the above two codes, this will print in your template will be

questions/2345708

and another way to get a full URL is:

  • {{request.build_absolute_uri}}

this will print in your template will be:

https://stackoverflow.com/questions/2345708
santosh Chauhan
  • 47
  • 2
  • 15
1

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.

aris
  • 22,725
  • 1
  • 29
  • 33
1
<div class='col-12 col-md-6'>
    <p class='lead'>Login</p>
    {% include 'accounts/snippets/form.html' with form=login_form next_url=request.build_absolute_uri %}
</div>

Here for example am saying load the form and tell the form that the next URL is the current URL which this code rendred from

Fahd Mannaa
  • 295
  • 2
  • 7
1

I use this code :

request.build_absolute_uri('/')[:-1]

response :

https://yourdomain.com
Radesh
  • 13,084
  • 4
  • 51
  • 64
0

request.get_host() will give you the domain.

Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162
Roge
  • 3,294
  • 2
  • 20
  • 19
0
request.get_host()

Use this for request object for APIView in django

Aditya Joardar
  • 580
  • 5
  • 11
0

If request is around, there are good answers. There's also good answers on how to obtain it in the template. Here a simple option for accessing the url in the backend.

in settings.py:

ABSOLUTE_URL_BASE = "http://yourdomain.com"

in models.py or wherever:

from django.conf import settings
from django.urls import reverse

url = settings.ABSOLUTE_URL_BASE + reverse("app:name")

One could also use this set-up to create a templatetag.

-2
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]
-3

You can also use:

import socket
socket.gethostname()

This is working fine for me,

I'm not entirely sure how it works. I believe this is a bit more low level and will return your server hostname, which might be different than the hostname used by your user to get to your page.

Eduardo
  • 22,574
  • 11
  • 76
  • 94
  • Yeah..you pointed out the problem. Hostname is not necessarily the same as the domain name. – mpen Apr 19 '16 at 05:04
  • This solves a very different problem. Consider a shared hosting server with multiple websites - using the code above, all sites generating URLs will have all such URLs pointing to the host machine, which is likely NOT any of the running websites. – tbm Aug 12 '16 at 15:32
-5

You can try "request.get_full_path()"

Max Ferreira
  • 468
  • 1
  • 4
  • 19