Django: How do I pass request URLs through urls.py? - django

I have a django app with the following entries related to logging out in urls.py:
(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page':'/logout_page/'}),
(r'^logout_page/$', 'django.views.generic.simple.direct_to_template', {'template':'registration/logged_out.html'}),
I want to add a "Log back in" link in logged_out.html, which requires me to know the location (URL) from where the user originally logged out.
How do I reference that URL in the logout dispatcher entry, and how do I pass it to the logout_page entry?
Thanks in advance

I do not exactly understand what you mean by "passing variables here" but you can pass variables to the views from urls like this
r'^(category/page/(?P[0-9]+)/$
page_number is passed as a variable to the url
But what I think you are looking for is to pass the previous prefer which you should be able access in the template like this :
{{ request.META.HTTP_REFERER }}
So you can do something like :
Login
in the template of logout_page to achieve what you are looking for

Related

LOGIN_REDIRECT_URL in django

I am very new to Django and I'm nearing the end of the django girls tutorial. I have added "#login_required" above my post_detail in views (view for clicking on a specific post) and added a login.html template. So when I click on a post title I get redirected to my login page (so far, so good) and the url is: http://127.0.0.1:8000/accounts/login/?next=/post/11/ (trying this on my computer atm.)
Then I type in my admin name/password and automatically get redirected to http://127.0.0.1:8000/accounts/profile/ and of course get a "Page not found (404)" (since I have no url/view/template for that). I thought "Dang, I just wanted to be redirected to /post/11/"!
Looked around on stack overflow and found this question:
Signing in leads to "/accounts/profile/" in Django (sounds about right)
and got the answer
Change the value of LOGIN_REDIRECT_URL in your settings.py.
So I looked up LOGIN_REDIRECT_URL in the Django documentation:
Default: '/accounts/profile/'
The URL where requests are redirected after login when the contrib.auth.login view gets no next parameter.
This is used by the login_required() decorator, for example.
This setting also accepts named URL patterns which can be used to reduce configuration duplication since you don’t have to define the URL in two places (settings and URLconf).
Deprecated since version 1.8: The setting may also be a dotted Python path to a view function. Support for this will be removed in Django 1.10.
But doesn't my contrib.auth.login get a next parameter? (looking at my url that say "?next=/post/11/" at the end) Please help me out here, I'm lost for what the problem could be here :(
You can view the page at:
http://finbel.pythonanywhere.com/
And the source code at:
https://github.com/Finbel/my-first-blog
UPDATE (1):
So I now know that the LOGIN_REDIRECT_URL is the thing that's deciding where I end up next, which must mean that it ignores the next-parameter in the url. I googled further on the problem and found this question which was very similar to my problem, i.e.
Documentation states that I need to use the "next" parameter and context processors. I have the {{next}} in my template, but I'm confused on how to actually pass the "/gallery/(username)". Any help would be greatly appreciated.
(I don't even have the {{next}} in my template, where/how should I add it?)
The preferred answer to that question seemed to be:
Django's login view django.contrib.auth.views.login accepts a dictionary named extra_context. The values in the dictionary are directly passed to the template. So you can use that to set the next parameter. Once that is done, you can set a hidden field with name next and value {{ next }} so that it gets rendered in the template.
But I'm not sure how to interpret this. While writing this edit I got an answer on this post (by kacperd) and will read it through now)
The problem is that contrib.auth.login doesn't get the next parameter.
When you try to get the login_required view without credentials your request is redirect to login view, and the template you created is rendered. The next parameter is present in this view, but when you perform the login action which is submitting the form, you are not including next in your request so contrib.auth.login doesn't get it and redirects to default page.
The solution to your problem is to include the next param and pass it forward. You can do this by modifying your login template. Simply add ?next={{ request.GET.next }} to form action attribute.
<form method="post" action="{% url 'django.contrib.auth.views.login' %}?next={{ request.GET.next }}">

Django: How to return to previous URL

Novice here who learned to develop a web app with python using Flask. Now I'm trying to learn django 1.9 by redoing the same app with django.
Right now I am stuck at trying to get the current URL and pass it as an argument so that the user can come back once the action on the next page is completed.
In Flask, to return to a previous URL, I would use the 'next' parameter and the request.url to get the current url before changing page.
In the template you would find something like this:
Buy punchcard :
and in the view:
redirect(request.args.get("next"))
I thought it would be about the same with django, but I cannot make it work. I did find some suggestions, but they are for older django version(older than 1.5) and do not work anymore(and they are pretty convulsed as solutions goes!)
Right now, in my view I am using
return redirect(next)
Note: The use of return redirect in django seems very recent itself if I judge by solutions on the web that always seem to use return HttpResponse(..., so I take it alot of changes happened lately in how to do things.
and in the template I have
<a href="{% url 'main:buy_punchcard' member.id next={{ request.path }} %}">Buy punchcard</p>
but this actually return an error
Could not parse the remainder: '{{' from '{{'
I did add the context_processors in settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
)
But this is only the last error in a very long streak of errors. Bottom line is, I can't make it work.
As such, anyone could point me in the right direction as to what is the way to do this in django 1.9? It look like a pretty basic function so I thought it would be easier somehow.
If you want next to be included in the query string, then move it outside of the url tag:
<a href="{% url 'main:buy_punchcard' member.id %}?next={{ request.path }}">Buy punchcard</p>
In your view, you can fetch next from request.GET, and return the redirect response using either HttpResponseRedirect or the redirect shortcut.
from django.utils.http import is_safe_url
next = request.GET.get('next', '/default/url/')
# check that next is safe
if not is_safe_url(next):
next = '/default/url/'
return redirect(next)
Note that it might not be safe to redirect to a url fetched from the query string. For example, it could link to a different domain. Django has a method is_safe_url that it uses to check next urls when logging in or out.
You don't need {{ }} there, just:
<a href="{% url 'main:buy_punchcard' member.id next=request.path %}">Buy punchcard</p>

What is reverse()?

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

How to get the previous URL in Django

I'll give the step by step info.
Let's say we're in the about page, the URL is example.com/about.
There's an email a friend button, when clicked, the URL is example.com/emailafriend.
Then when I clicked the submit button, the referal URL will be submitted is example.com/emailafriend. Question, how to get the about page URL? BTW, I'm using request.META['HTTP_REFERER'].
You could work with the request path attribute and add that to your form's action attribute, like so:
<form method="post" action="/tell-a-friend?return_url={{ request.path }}">
...
</form>
Then, in your tell-a-friend view, HttpResponseRedirect to the return_url query parameter.
It looks like maybe you are using APPEND_SLASH which will do a redirect if you don't have a slash. Try changing the button link to sample.com/emailafriend/, note the ending slash.
Maybe simply pass sample.com/about as GET or POST param? Or even through session.

How to debug Django NoReverseMatch errors?

Folks, I am getting a NoReverseMatch error for a particular url call.
I'd like to know: are there any good tools to debug these in general? For example, some way to list out which URLs are registered?
My particular example:
Template:
<a href=
"{% url django.contrib.auth.views.redirect_to_login blarg %}">log in</a>
Error:
NoReverseMatch: Reverse for
'settings.django.contrib.auth.views.redirect_to_login'
with arguments '('[[ UNDEFINED VARIABLE ]]',)'
and keyword arguments '{}' not found.
I am using Google App Engine with appenginepatch, so Django itself is modified.
In this particular case where the url reversal uses the full path to the view function, the easy thing to do is just go look at the view function. In Django-1.1 this looks like:
def redirect_to_login(next, login_url=None,
redirect_field_name=REDIRECT_FIELD_NAME):
"Redirects the user to the login page, passing the given 'next' page"
if not login_url:
login_url = settings.LOGIN_URL
return HttpResponseRedirect('%s?%s=%s' % (login_url,
urlquote(redirect_field_name),
urlquote(next)))
This function does not even take a request object, so its really not even a proper view, and it is not even registered in django/contrib/auth/urls.py. That means it's probably only meant to be used as a helper function in other views.
In any case, based on your particular example, what you probably wanted to do was use the plain old login url like so:
<a href="{% url django.contrib.auth.views.login %}?next={{request.path}}">
log in
</a>
Also, I believe if you set TEMPLATE_DEBUG = True in your settings, you will get a list of all the url patterns django checked against before throwing the error.