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.
>>> 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'
If you want to use it with reverse() you can do this : request.build_absolute_uri(reverse('view_name', args=(obj.pk, )))
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>
In your view, just do this:
base_url = "{0}://{1}{2}".format(request.scheme, request.get_host(), request.path)
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.
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.
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 }}
Examine Request.META dictionary that comes in. I think it has server name and server port.
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.
Try the following code:
{{ request.scheme }}://{{ request.META.HTTP_HOST }}
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
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 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/
I got it:
wsgiref.util.request_uri(request.META)
Get the full uri with schema, host, port path and query.
You can either pass request reverse('view-name', request=request) or enclose reverse() with build_absolute_uri request.build_absolute_uri(reverse('view-name'))
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') }"
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!
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.
{{ request.path }} #Without GET parameters.
{{ 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
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.
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'))
<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
I use this code :
request.build_absolute_uri('/')[:-1]
response :
https://yourdomain.com
request.get_host() will give you the domain.
request.get_host()
Use this for request object for APIView in django
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]
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.
You can try "request.get_full_path()"
Related
I'm trying to send an object obtained with a ModelChoiceField into a view via urls and views parameters by I don't think that's the right method. I don't know if I have to use POST or GET method.
When I use the GET method, the object is displayed in the url but the view stay the same.
With the POST method, django send errors messages about parameters.
EDIT : I don't remeber the exact messages, I can't recover them for now but as I remember they said something like Reverse for argument not found
My form
class IndividuForm(forms.Form):
individu = forms.ModelChoiceField(queryset=Individu.objects.all()
Here's the view with the form
def index(request):
individu_form = IndividuForm(request.GET or None)
if individu_form.is_valid():
individu_id = individu_form.cleaned_data['individu'].id
HttpResponseRedirect('%i/raison/' % individu_id)
return render(request, 'derog_bv/index.html', {'individu_form':individu_form})
The template where the form is displayed
<form action="{% url 'index' <!-- Errors when I put parameters here --> %} method="<!-- GET or POST -->">
{% csrf_token %}
{{ form }}
<input type="submit">Suivant</input>
</form>
The view where I want to get my object
def raison(request, """ error with this parameter : individu_id"""):
individu = get_object_or_404(Individu, pk=individu_id)
URLs
urlpatterns = [
path('', views.index, name='index'),
path('<int:individu_id>/raison/', views.raison, name='raison'),
]
Ok, so:
1/ you definitly want to use the GET method for your form (you're not submitting data for processing / server state change)
2/ I don't know why you'd want to pass parameters (nor which parameters FWIW) to the {% url %} tag in your index template - you're submitting the form to the index view, which doesn't expect any additional param. Actually you could just remove the action attribute of the HTML form tag since the default (submitting to the current url) is what you want.
3/ your views.raison prototype must match the url definition, so it has to be:
def raison(request, individu_id):
# code here
4/ in your index view:
HttpResponseRedirect('%i/raison/' % individu_id)
you want to build the url using django.core.urlresolvers.reverse instead :
from django.core.urlresolvers import reverse
def index(request):
# code here
if ...:
next = reverse("raison", kwargs={"individu_id": individu_id})
return HttpResponseRedirect(next)
or - even easier - just use the redirect shortcut:
from django.shortcuts import redirect
def index(request):
# code here
if ...:
return redirect("raison", individu_id=individu_id)
There might be other issues with your code but from the infos you posted, those hints should at least put you back on tracks.
I have url:path('<int:id>',views.article_detail,name="detail")
It works on the site.
<p>{{article.title}} </p>
But if I try to give a link in the email, for example
<p>article </p>
In link I heve only
http://articles/36
Link like
<p>artickle! </p>
not work.
You have two ways to accomplish what you need.
If it’s just one time work then I suggest something quick:
On your view import the settings and send to the template the ‘settings.BASE_URL’ value
from django.conf import settings
base_url = settings.BASE_URL
you can pass the value in your context to create the url as you already tried to do.
Another option is to create a tag to get the base url from the setting and generate the complete url base on the given parameter
from django import template
from django.conf import settings
base_url = settings.BASE_URL
register = template.Library()
#register.simple_tag
def add_domain(partial_url):
return base_url + partial_url
In your template just use
{% add_domain url_generated %}
This is just an example, you could define a more complex tag to generate the url included the domain with a flag parameter.
Most of views in my django app use #login_required decorator. Also, I have three different login urls. Views have corresponding login urls hardcoded into their #login_required decorators.
#login_required('myapp/logintype1'):
def usertype1_home(request):
# Further dode
# ...
#login_required('myapp/logintype2'):
def usertype2_home(request):
# Further code
# ...
Since the number of such views is quite large, whenever I change login url in urls.py I have to change login-url in all the decorators. I want to use something like {% urls 'urlpatter1' %} and {% urls 'urlpatter2' %}. Can I use reverse?
How I can use named url patterns instead of hard coding url patterns in #login_required decorator?
Somewhere in the top of views.py after import ... statements add something like this
login_type1 = reverse_lazy('urlpatter1') # or LOGIN_TYPE1
login_type2 = reverse_lazy('urlpatter2') # or LOGIN_TYPE2
And use these variables later
#login_required(login_url=login_type1)
...
UPDATE: reverse was replaced with reverse_lazy as #Alasdair suggested. See docs (2nd point).
I am new to python and django and i have a question regarding the redirect function.
This is a reduced version of my views.py file.
def page_index(request, error_message=''):
print error_message
def add_page(request):
return redirect('page_index') # this work fine
return redirect('page_index', error_message='test') # this does not work
And here is a short version of my urls.py
urlpatterns = patterns(
'x.views',
url(r'^$', 'page_index', {'error_message': 't'}, name='page_index'),
url(r'^add/$', 'add_page', name='add_page'),
)
When i try redirecting to page_index without the keyword argument everything works fine, but when i use the kwag i get the following error message:
NoReverseMatch at /pages/add/
Reverse for 'page_index' with arguments '()' and keyword arguments '{'error_message': 'test'}' not found.
What am i doing wrong?
Short answer: There is no place in your url scheme for the 'error_message' keyword.
Longer answer:
The redirect() function is calling reverse() to build a URL; it is going to send the user's browser to that URL by returning an HTTP response with a 302 redirect status code, and the new url. Any keyword arguments that you supply to reverse() are supposed to end up as part of the url -- that's how they get communicated to the user.
In your case, though, the url for 'page_index` is just defined as '^$' -- this is the root url, which looks like 'http://yoursite.com/' in the browser.
If you want to be able to issue a redirect that contains other information, you will need to define a place for it in the url, or add it in a different way.
TwoThree ways are fairly common for this:
Use a query parameter -- this sends the message to the client explicitly; if you aren't careful, people can craft urls to make your index page say whatever they want it to.
return redirect(reverse('page-index')+"?error_message=test"))
Stash the message in the session and pull it out when the next page loads -- this requires that you have sessions configured, and keeps track of everything on the server side, rather than relying on the client to send you back the error message:
def add_page(request):
request.session['error_message'] = 'test'
return redirect('page-index')
def page_index(request):
print request.session.get('error_message','')
Use the messages framework for this -- this is preferred over ad-hoc session attributes, as long as you don't need too many 'types' of message on the same page. If all you have is a space in your template for error message, though, then this is really easy:
from django.contrib.messages import error
def add_page(request):
error(request, 'test')
return redirect('page-index')
And then in your base template, have a block like this somewhere (probably more complex than this; styled, even):
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
In bothall cases, though, you can remove the arguments from your urls.py -- the message itself is not going to be part of the path component of the URL.
urlpatterns = patterns(
'x.views',
url(r'^$', 'page_index', name='page_index'),
url(r'^add/$', 'add_page', name='add_page'),
)
You could either create a named url pattern for each error message, and pass it in the args dict, like this:
url(r'^$', 'page_index', {'error_message': 'test'}, name='test_page_index'),
or if you are willing to put the error message in the actual url you could opt for this:
url(r'(?P"<"error_message">"\w+)/^$', 'page_index', name='page_index'),
(PLEASE REMOVE THE "" around the "<" and ">", had to put them in brackets or they would be excluded form the answer, I guess everything that looks like an html tags is filtered out)
and in the view:
return redirect(reverse('page_index', args=['test']))
redirect has to redirect to an actual url (it's an HTTP redirect, not just a Python call).
There is no 'error_message' segment in your url conf, even though you have set it up so that by default the view function is called with error_message as a kwarg.
Since this is your index page and you probably don't want to add extra path segments to the url you could maybe hack it up using GET vars. eg
def page_index(request):
if 'error_message' in request.GET:
print request.GET['error_message']
from django.core.urlresolvers import reverse
def add_page(request):
return redirect('%s?error_message=test' % reverse('page_index'))
urlpatterns = patterns(
'x.views',
url(r'^$', 'page_index', name='page_index'),
url(r'^add/$', 'add_page', name='add_page'),
)
updated:
Thinking about this as I walked home, although your question is about the redirect and kwargs etc, arguably the better option here would be to use Django's messages framework and leave error_message entirely out of your view kwargs, GET variables etc.
eg
from django.contrib import messages
def add_page(request):
messages.add_message(request, messages.ERROR, 'test')
return redirect('page_index')
This also avoids people being able to craft urls with their own made up error messages in them, which could be a security risk.
You can user django messages. Example:
from django.contrib.auth import messages
messages.info(request, False, "success")
return redirect("app:url")
Then in html, you can use them using custom filter to get messages value based on key, as messages are nothing but dictionary.
{% with messages_dict|get_value_from_dict:'success' as success %}
{% if sucess %}
$('#notification_modal').modal('open');
{% endif %}
{% endwith %}
Custom filter which can be included in templatetags folder under app
from django import template
register = template.Library()
#register.filter('get_value_from_dict')
def get_value_from_dict(dict_data, key):
"""
usage example {{ your_dict|get_value_from_dict:your_key }}
"""
if key:
return dict_data.get(key)
else:
return None
Before Django 1.0 there was an easy way to get the admin url of an object, and I had written a small filter that I'd use like this: <a href="{{ object|admin_url }}" .... > ... </a>
Basically I was using the url reverse function with the view name being 'django.contrib.admin.views.main.change_stage'
reverse( 'django.contrib.admin.views.main.change_stage', args=[app_label, model_name, object_id] )
to get the url.
As you might have guessed, I'm trying to update to the latest version of Django, and this is one of the obstacles I came across, that method for getting the admin url doesn't work anymore.
How can I do this in django 1.0? (or 1.1 for that matter, as I'm trying to update to the latest version in the svn).
You can use the URL resolver directly in a template, there's no need to write your own filter. E.g.
{% url 'admin:index' %}
{% url 'admin:polls_choice_add' %}
{% url 'admin:polls_choice_change' choice.id %}
{% url 'admin:polls_choice_changelist' %}
Ref: Documentation
from django.core.urlresolvers import reverse
def url_to_edit_object(obj):
url = reverse('admin:%s_%s_change' % (obj._meta.app_label, obj._meta.model_name), args=[obj.id] )
return u'Edit %s' % (url, obj.__unicode__())
This is similar to hansen_j's solution except that it uses url namespaces, admin: being the admin's default application namespace.
I had a similar issue where I would try to call reverse('admin_index') and was constantly getting django.core.urlresolvers.NoReverseMatch errors.
Turns out I had the old format admin urls in my urls.py file.
I had this in my urlpatterns:
(r'^admin/(.*)', admin.site.root),
which gets the admin screens working but is the deprecated way of doing it. I needed to change it to this:
(r'^admin/', include(admin.site.urls) ),
Once I did that, all the goodness that was promised in the Reversing Admin URLs docs started working.
Essentially the same as Mike Ramirez's answer, but simpler and closer in stylistics to django standard get_absolute_url method:
from django.urls import reverse
def get_admin_url(self):
return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name),
args=[self.id])
Using template tag admin_urlname:
There's another way for the later versions (>=1.10), recommend by the Django documentation, using the template tag admin_urlname:
{% load admin_urls %}
Add user
Delete this user
Where opts is something like mymodelinstance._meta or MyModelClass._meta
One gotcha is you can't access underscore attributes directly in Django templates (like {{ myinstance._meta }}) so you have to pass the opts object in from the view as template context.
For pre 1.1 django it is simple (for default admin site instance):
reverse('admin_%s_%s_change' % (app_label, model_name), args=(object_id,))
I solved this by changing the expression to:
reverse( 'django-admin', args=["%s/%s/%s/" % (app_label, model_name, object_id)] )
This requires/assumes that the root url conf has a name for the "admin" url handler, mainly that name is "django-admin",
i.e. in the root url conf:
url(r'^admin/(.*)', admin.site.root, name='django-admin'),
It seems to be working, but I'm not sure of its cleanness.
If you are using 1.0, try making a custom templatetag that looks like this:
def adminpageurl(object, link=None):
if link is None:
link = object
return "%s" % (
instance._meta.app_label,
instance._meta.module_name,
instance.id,
link,
)
then just use {% adminpageurl my_object %} in your template (don't forget to load the templatetag first)
For going to the admin page or admin login page we can use the below link. It works for me -
{% url 'admin:index' %}
This url takes me directly to the admin page.
Here's another option, using models:
Create a base model (or just add the admin_link method to a particular model)
class CommonModel(models.Model):
def admin_link(self):
if self.pk:
return mark_safe(u'<a target="_blank" href="../../../%s/%s/%s/">%s</a>' % (self._meta.app_label,
self._meta.object_name.lower(), self.pk, self))
else:
return mark_safe(u'')
class Meta:
abstract = True
Inherit from that base model
class User(CommonModel):
username = models.CharField(max_length=765)
password = models.CharField(max_length=192)
Use it in a template
{{ user.admin_link }}
Or view
user.admin_link()