What is reverse()? - django

When I read Django code sometimes, I see in some templates reverse(). I am not quite sure what this is but it is used together with HttpResponseRedirect. How and when is this reverse() supposed to be used?

reverse() | Django documentation
Let's suppose that in your urls.py you have defined this:
url(r'^foo$', some_view, name='url_name'),
In a template you can then refer to this url as:
<!-- django <= 1.4 -->
link which calls some_view
<!-- django >= 1.5 or with {% load url from future %} in your template -->
link which calls some_view
This will be rendered as:
link which calls some_view
Now say you want to do something similar in your views.py - e.g. you are handling some other URL (not /foo/) in some other view (not some_view) and you want to redirect the user to /foo/ (often the case on successful form submission).
You could just do:
return HttpResponseRedirect('/foo/')
But what if you want to change the URL in the future? You'd have to update your urls.py and all references to it in your code. This violates the DRY (Don't Repeat Yourself) principle and the whole idea of editing in one place only - which is something to strive for.
Instead, you can say:
from django.http import HttpResponseRedirect
from django.urls import reverse
return HttpResponseRedirect(reverse('url_name'))
This looks through all URLs defined in your project for the URL defined with the name url_name and returns the actual URL /foo/.
This means that you refer to the URL only by its name attribute - if you want to change the URL itself or the view it refers to you can do this by editing one place only - urls.py.

The existing answers are quite clear. Just in case you do not know why it is called reverse: It takes an input of a url name and gives the actual url, which is reverse to having a url first and then give it a name.

Existing answers did a great job at explaining the what of this reverse() function in Django.
However, I'd hoped that my answer shed a different light at the why: why use reverse() in place of other more straightforward, arguably more pythonic approaches in template-view binding, and what are some legitimate reasons for the popularity of this "redirect via reverse() pattern" in Django routing logic.
One key benefit is the reverse construction of a url, as others have mentioned. Just like how you would use {% url "profile" profile.id %} to generate the url from your app's url configuration file: e.g. path('<int:profile.id>/profile', views.profile, name="profile").
But as the OP have noted, the use of reverse() is also commonly combined with the use of HttpResponseRedirect. But why?
I am not quite sure what this is but it is used together with HttpResponseRedirect. How and when is this reverse() supposed to be used?
Consider the following views.py:
from django.http import HttpResponseRedirect
from django.urls import reverse
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected = question.choice_set.get(pk=request.POST['choice'])
except KeyError:
# handle exception
pass
else:
selected.votes += 1
selected.save()
return HttpResponseRedirect(reverse('polls:polls-results',
args=(question.id)
))
And our minimal urls.py:
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('<int:question_id>/results/', views.results, name='polls-results'),
path('<int:question_id>/vote/', views.vote, name='polls-vote')
]
In the vote() function, the code in our else block uses reverse along with HttpResponseRedirect in the following pattern:
HttpResponseRedirect(reverse('polls:polls-results',
args=(question.id)
This first and foremost, means we don't have to hardcode the URL (consistent with the DRY principle) but more crucially, reverse() provides an elegant way to construct URL strings by handling values unpacked from the arguments (args=(question.id) is handled by URLConfig). Supposed question has an attribute id which contains the value 5, the URL constructed from the reverse() would then be:
'/polls/5/results/'
In normal template-view binding code, we use HttpResponse() or render() as they typically involve less abstraction: one view function returning one template:
def index(request):
return render(request, 'polls/index.html')
But in many legitimate cases of redirection, we typically care about constructing the URL from a list of parameters. These include cases such as:
HTML form submission through POST request
User login post-validation
Reset password through JSON web tokens
Most of these involve some form of redirection, and a URL constructed through a set of parameters. Hope this adds to the already helpful thread of answers!

This is an old question, but here is something that might help someone.
From the official docs:
Django provides tools for performing URL reversing that match the
different layers where URLs are needed: In templates: Using the url
template tag. In Python code: Using the reverse() function. In higher
level code related to handling of URLs of Django model instances: The
get_absolute_url() method.
Eg. in templates (url tag)
2012 Archive
Eg. in python code (using the reverse function)
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))

The function supports the dry principle - ensuring that you don't hard code urls throughout your app. A url should be defined in one place, and only one place - your url conf. After that you're really just referencing that info.
Use reverse() to give you the url of a page, given either the path to the view, or the page_name parameter from your url conf. You would use it in cases where it doesn't make sense to do it in the template with {% url 'my-page' %}.
There are lots of possible places you might use this functionality. One place I've found I use it is when redirecting users in a view (often after the successful processing of a form)-
return HttpResponseRedirect(reverse('thanks-we-got-your-form-page'))
You might also use it when writing template tags.
Another time I used reverse() was with model inheritance. I had a ListView on a parent model, but wanted to get from any one of those parent objects to the DetailView of it's associated child object. I attached a get__child_url() function to the parent which identified the existence of a child and returned the url of it's DetailView using reverse().

There is a doc for that
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-resolution-of-urls
it can be used to generate an URL for a given view
main advantage is that you do not hard code routes in your code.

The reverse() is used to adhere the django DRY principle i.e if you change the url in future then you can reference that url using reverse(urlname).

Related

Django: what is the difference between redirect to a view function and redirect to a url (from urls.py file)

Which is better/standard practice?
return redirect('index')
return redirect('/users/new')
index is view function
/users/new is urlpatterns from urls.py
Using URLs directly is discouraged by Django's documentation (my bold):
A common need when working on a Django project is the possibility to obtain URLs in their final forms either for embedding in generated content (views and assets URLs, URLs shown to the user, etc.) or for handling of the navigation flow on the server side (redirections, etc.)
It is strongly desirable to avoid hard-coding these URLs (a laborious, non-scalable and error-prone strategy). Equally dangerous is devising ad-hoc mechanisms to generate URLs that are parallel to the design described by the URLconf, which can result in the production of URLs that become stale over time.
In other contexts you may wish to use reverse() or {% url %}, or to add a get_absolute_url() method to your models.
I think that index in your example is not a view function, but a url-name
url(r'^some/url/to/index', views.index_2, name='index')
view functions can have index_2 name and any url path, but you use "index" to redirect, for example return redirect(reverse('index')).
As you can see, redirect only accept an url path, then reverse function receive a url name and return an url path, in the example reverse will return "some/url/to/index"

Use anchored urls in django (anchored by the id of http objects)

My level in front-end development is pretty low.
Nevertheless, I want to implement the very wide-spread behaviour of having several parts anchored in one single page instead of several separate pages, and refer to these parts in the url.
So instead of having mysite.com/how_to_walk and mysite.com/how_to_run as two different pages and templates, I would like to have one page mysite.com/how_to_do_stuff and then depending on if you want to #walk or #run, refer to the html headers with the id field as suffixes of the url.
I don't really know how to do it with django. I'd like to create only one url dispatcher that - I guess - will look like that:
url(r'^how_to_stuff/#(?P<partId>[-\w]*)', views.how_to, name='how_to')
...and then I have to create a simple view, but how to refer to the id in the render() call, I have no idea.
I found the answer to my own question. The crucial element is that when the client (browser) goes for such an anchored url mysite.com/how_to_do_stuff#run, it sends to the server only the root url mysite.com/how_to_do_stuff and then applies the anchor to it locally. So you need:
A classic, simple url/view/template combination that loads the page mysite.com/how_to_do_stuff when it is asked by the client.
A way to send the client to these anchored pages and reference them for development. I do this through an other url/view couple that redirects the client to the right anchored url.
Below is the result:
In urls.py:
...
url(r'^how_to_do_stuff/(?P<part_id>[-\w]+)', views.how_to_redirect, name='how_to'),
url(r'^how_to_do_stuff', views.how_to)
In views.py:
def how_to_redirect(request, part_id):
return HttpResponseRedirect("/how_to_do_stuff/#"+part_id)
def how_to(request):
return render(request, "GWSite/how_to_do_stuff.html")
And then I refer to these in my templates through:
{% url "how_to" "run"}
From django project website
Take a look at how you they send the num var to views.py
# URLconf
from django.conf.urls import url
urlpatterns = [
url(r'^blog/$', 'blog.views.page'),
url(r'^blog/page(?P<num>[0-9]+)/$', 'blog.views.page'),
]
# View (in blog/views.py)
def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
...

Is it possible to give external URLs names in django

I have just started naming my URL patterns in Django, so that if I want to change the URL pattern, I just have to change it in one place. e.g:
url(r'^$', HomeListView.as_view(), name='home'),
And referencing it in my templates like this:
{% url home %}
Is this possible with external links in case they change or I change the a Facebook page link for example. How would this look?
Thanks
One way to do this could be to write an external_url template tag, and have an ExternalURL model that stores them.
This would give you the advantage of being able to have the urls editable without redeploying changed code.
The disadvantage is that there will be database lookups to see those urls. Also, you would need to {% load external_urls %} in templates you want to use it in.
# models.py pseudo-code
class ExternalURL(models.Model):
name = models.CharField(unique=True)
url = models.URLField()
Your template tag might look something like:
# app/templatetags/external_url.py
#library.register()
def external_url(name):
try:
return ExternalURL.objects.get(name=name).url
except ExternalURL.DoesNotExist:
return ''
Another alternative could be to have a Context Processor that stores them all in the context, allowing you to not have to pass them explicitly into views: would be useful if you had several external urls that were used in many places within your system.
def external_urls(request):
return {
'google': 'http://www.google.com/',
# more here
}
The advantages of this is no database lookup, no requirement to load the template tag, but you will need to add it to your settings.CONTEXT_PROCESSORS. Also, you could inspect request to see if the current user may see all the urls.
If its external links, and possibility of the change then you should define it in settings or separate static url file and pass those variable with request context.
urls.py should be recommended to use only for your app specific urls.
In this SO thread you can see how to approach defining constant

Django1.4: Generic way to set language links in template to work with i18n_patterns?

I started to play with new i18n_patterns in Django 1.4. Basically, i want to have language links for each of my supported languages on all of my templates headers. I have implemented my header as a separate template that is being included in other templates.
Is there a way to keep my header generic and solve this without passing the current view name or current url in template context? I guess it comes to a question how do i retrieve the current view or url from inside the template in a generic way.
BTW, i discovered that my previous approach with set_lang view to change the active language using the referrer will be broken with url_patterns as after changing the language it will change it back when redirected to the referred view.
Any help figuring out the common approach to set language links in templates to be used with url_patterns in a generic way would be appreciated!
Basically, there are two different approaches to setting the language. You can use i18n_patterns to auto-magically prefix your urls with a language code, or you can use the django.views.i18n.set_language view to change the value of the language code in the user's session (or a cookie, if your project doesn't have session support).
It's worth noting the algorithm LocaleMiddleware uses to determine language:
First, it looks for the language prefix in the requested URL. This is only performed when you are using the i18n_patterns function in your root URLconf. See Internationalization: in URL patterns for more information about the language prefix and how to internationalize URL patterns.
Failing that, it looks for a django_language key in the current user's session.
Failing that, it looks for a cookie.The name of the cookie used is set by the LANGUAGE_COOKIE_NAME setting. (The default name is django_language.)
Failing that, it looks at the Accept-Language HTTP header. This header is sent by your browser and tells the server which language(s) you prefer, in order by priority. Django tries each language in the header until it finds one with available translations.
Failing that, it uses the global LANGUAGE_CODE setting.
The problem you're likely running into is that you can't use set_language to redirect from a url that's already being served with a language prefix, unless you specifically pass a next parameter in the POST data. This is because set_language will default to redirecting to the referrer, which will include the previous language prefix, which LocaleMiddleware will then see and serve the content in the old language (because it looks for a language prefix in the url before checking the django_language session variable).
An example, for clarity:
Your user is on /en/news/article/1000, and clicks on the link which will post 'language=es' to set_language.
set_language sees 'language=es', checks to see if 'es' is available, and then sets the 'django_language' session variable (or cookie) to 'es'
Since you haven't set 'next', it redirects to the value of reqeuest.META['HTTP_REFERRER'], which is /en/news/article/1000
LocaleMiddleware (source) sees the 'en' prefix in the url, and activates the 'en' language and sets request.LANGUAGE_CODE to 'en'
I see two possible solutions:
Write your own set_language view (see the original source here), which will check for a language prefix in the referrer(use django.utils.translation.get_language_from_path), and change it to the prefix for the newly selected language before redirecting back to it.
Use javascript to do the same operation client-side, and set the next POST parameter. Really this is kind of silly; it would probably be simpler to just use javascript to dynamically prepend all urls with the user's preferred language code, and forget about set_language altogether.
It seems that this new set_language view should probably be Django's default behavior. There was a ticket raised, which included a proposed implementation, but didn't really describe the problem and was subsequently closed. I suggest opening a new ticket with a better description of your use case, the problem caused by the existing set_language implementation, and your proposed solution.
Actually, there's no need to fiddle with your view. Django has a handy slice tag, so you can just use {{ request.path|slice:'3:' }} as your link URL. This lops the language code prefix off so the language is set by the set_lang function.
Having had the same problem today with Django 1.7, I devised this solution - not very DRY but it seems to work OK (and all my tests are passing, so...).
Rather than using the builtin set_language view, I copied it and made one tiny adjustment - here's the result:
def set_language(request):
"""
Redirect to a given url while setting the chosen language in the
session or cookie. The url and the language code need to be
specified in the request parameters.
Since this view changes how the user will see the rest of the site, it must
only be accessed as a POST request. If called as a GET request, it will
redirect to the page in the request (the 'next' parameter) without changing
any state.
"""
next = request.POST.get('next', request.GET.get('next'))
if not is_safe_url(url=next, host=request.get_host()):
next = request.META.get('HTTP_REFERER')
if not is_safe_url(url=next, host=request.get_host()):
next = '/'
lang_code = request.POST.get('language', None)
# Start changed part
next = urlparse(next).path # Failsafe when next is take from HTTP_REFERER
# We need to be able to filter out the language prefix from the next URL
current_language = translation.get_language_from_path(next)
translation.activate(current_language)
next_data = resolve(next)
translation.activate(lang_code) # this should ensure we get the right URL for the next page
next = reverse(next_data.view_name, args=next_data.args, kwargs=next_data.kwargs)
# End changed part
response = http.HttpResponseRedirect(next)
if request.method == 'POST':
if lang_code and check_for_language(lang_code):
if hasattr(request, 'session'):
request.session[LANGUAGE_SESSION_KEY] = lang_code
else:
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code,
max_age=settings.LANGUAGE_COOKIE_AGE,
path=settings.LANGUAGE_COOKIE_PATH,
domain=settings.LANGUAGE_COOKIE_DOMAIN)
return response
To sum it up, I resolve() the view parameters for next, then pass the data to reverse() after activating the new language. Hope this helps.
My apologies for the long delay. Thank you all for your answers.
First of all to comment on the two solution options by Chris:
Neither custom set_language nor javascript are good for the purpose as the whole beauty of url patterns is being SEO friendly.
Furthermore, simply replacing the prefix language in URL cannot be treated as full solution for urlpattern based urls as the whole URL might be translatable too. Ex: /en/profile/ for english and /fr/profil/ for french.
To solve such a problem one needs to capture the viewfunc and the arguments in order to reverse it for different language.
Fortunately for me, my project does not use translatable URLs for now and I took the following approach.
Add django.core.context_processors.request to TEMPLATE_CONTEXT_PROCESSORS so that request to be available in template rendering context.
Use RequestContext in views when rendering your views. This is always the case for me independent of the topic.
Write a quite simple templatetag that requires the context and takes an argument a language code to return the current URL in the given language. It basically makes sure the current URL has valid language-code prefix and simply returns another string that is the same current URL with replaced language-code.
ex:
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def get_current_url_for_lang(context, lang_code):
request=context.get('request',False)
if not request:
return None
request=context['request']
curr_url=request.path
if len(curr_url) < 4 or curr_url[0] != '/' or curr_url[3] != '/':
return curr_url
if context.get('LANGUAGES',False):
codes = []
for code,name in context['LANGUAGES']:
codes.append(code)
curr_langcode = curr_url[1:3]
if lang_code not in codes or curr_langcode not in codes:
return curr_url
changed_url = '/'+lang_code+curr_url[3:]
return changed_url
Furthermore, if one does not like to inject full request into context it will be quite straightforward to write your own context_processor that simply pushes the request.path as current_url_path for instance and use that instead in your templatetag.
Your comments are welcomed as always!

Referring to databrowse urls in templates

I'd like to integrate django Databrowse into my application.
It comes down to pointing to databrowse urls from within template or view for enhanced drill down functionality of the databrowse.
Is there an easy way to extract the url from a databrowse object?
Well, one easy way would be to just construct the url you want, and pass it into the template:
databrowse_url = '/'.join((obj._meta.app_label, obj._meta.module_name, 'objects', str(obj.id)))
And then in the template (assuming you have databrowse situated at /databrowse:
<a href="/databrowse/{{ databrowse_url }}">
Which would give you a url like: /databrowse/app_name/model_name/objects/1.
You could recreate the databrowse urls in the format thats show in the databrowse urls.py
You might be able to get the url tag to work in your template by passing the view name + arguments.
However, if you browse the source, it looks like databrowse will add a 'url' attribute to the objects it works with.
EDIT:
Given an EasyModel instance, you can do the following:
my_easy_model_instance.url()
Most of the 'Easy' classes have a url() or urls() method on them.
Ended up writing mixin class that fetches relevant EasyInstance and reuses the url() of it:
from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse import site
class DatabrowseMixin:
def url(pyClass):
if not site.root_url:
#hack, but root_url is not set until the first databrowse view
#and resolving urlconf does not work either
site.root_url = '/databrowse/'
easy_model = EasyModel(site, pyClass.__class__)
obj = easy_model.object_by_pk(pyClass.pk)
return obj.url()
class MyModel(models.Model, DatabrowseMixin):
...
Now in my templates I can reuse my_model_instance.url tag pointing to databrowse object url.