Understanding django.shortcuts.redirect - python

I have a couple problems understanding how redirect or rather reverse really work.
In the main urls.py I have:
from django.conf.urls import patterns, include, url
from django.views.generic.simple import redirect_to
urlpatterns = patterns('',
url(r'^$', redirect_to, {'url': '/monitor/'}),
url(r'^monitor/', include('monitor.urls')),
)
and in monitors.urls I have:
from django.conf.urls import patterns, include, url
urlpatterns = patterns('monitor.views',
(r'^$', 'index'),
(r'^abc/(?P<id>.*$)', 'abc'),
)
When you call /monitor I want to redirect it to /monitor/abc so I did:
def index(request):
return redirect("abc")
def abc(render, id=None):
return render_to_response("monitor/list.htmld", {})
But I got an NoReverseMatch exception. But when I do:
def index(request):
return redirect("abc/")
then it suddenly works.
I cannot fully understand why. Why did reverse fail with abc but not with abc/? And how does reverse know that the redirect should include monitor/ as well? What if I had in the main urls.py another app called xyz which also has a abc view?

Why did reverse fail with 'abc' but not with 'abc/'?
Because it interpreted it as a view name (and you indeed have a view named 'abc', see your monitor.urls file). This means Django will call reverse to compute the URL. The value abc/ is interpreted as an actual URL which means Django won't call reverse to determine the URL.
This also explains why reverse failed: the view with name abc also requires an argument called id. Otherwise Django won't be able to lookup the URL as there is no view called abc without parameters.
Based on the documentation you should be able to reverse the URL using:
redirect("abc", id=...)
where ... is the value of the id parameter.
And how does reverse know that the redirect should include monitor/ as well?
That is because it knows what URLs are available and 1) it knows where the view called abc is defined and 2) it knows that monitors.urls is included with monitor/ in front.
What if I had in the main urls.py another app called "xyz" which also has a "abc" view?
In that case you have to use namespaces.

Related

Avoid Django form being resubmitted by using HttpResponseRedirect

My views.py runs code fine when I press a button on my HTML page, views.py:
def start_or_end_fast(request):
#If starting fast, add a row to the db:
#fast_finished = False
#start_date_time using = current time
#end_date_time using = current time
if request.method == 'POST' and 'start_fast' in request.POST:
add_fast = logTimes(fast_finished=False,start_date_time=datetime.now(),end_date_time=datetime.now())
add_fast.save()
print(add_fast.start_date_time,add_fast.end_date_time)
print('Fast started')
#return render(request,'startandstoptimes/index.html')
return HttpResponseRedirect('startandstoptimes/index.html')
You can see my commented return line, this works but when I refresh the page I can resubmit the data, I want to avoid this. In researching my solution, I saw this could be solved using HttpResponseRedirect but I am not able to get this to work with my code, the more I change the more broken things become.
My application urls.py:
from turtle import home
from django.urls import path,include
from . import views
urlpatterns = [
path('', views.start_or_end_fast,name="start_or_end_fast")
]
My project urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('startandstoptimes.urls'))
]
I believe it is related to the URLs, due to the 404 message I see:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/startandstoptimes/index.html
Using the URLconf defined in myfastingsite.urls, Django tried these URL patterns, in this order:
admin/
[name='start_or_end_fast']
The current path, startandstoptimes/index.html, didn’t match any of these.
Am I going down the right route trying to use HttpResponseRedirect or is there a better solution?
class HttpResponseRedirect¶
The first argument to the constructor is required – the path to redirect to. This can be a fully qualified URL (e.g.
'https://www.yahoo.com/search/'), an absolute path with no domain
(e.g. '/search/'), or even a relative path (e.g. 'search/'). In that
last case, the client browser will reconstruct the full URL itself
according to the current path. See HttpResponse for other optional
constructor arguments. Note that this returns an HTTP status code 302.
See this link for more details: docs
As what the documentation says, HttpResponseRedirect accepts URL and not the path of your template. You should be doing it something like this:
from django.urls import reverse
return HttpResponseRedirect(reverse('start_or_end_fast'))

django redirect inside of urls.py

I plan on having several different apps inside my django project, each for a new technology I want to play around with. As I work on each on, I want to have the root URL path redirect to the project I'm working on.
Directory Structure:
backyard/
my_project/
views.py
backyard/
urls.py
backyard/backyard/urls.py:
from django.conf.urls import patterns, include, url
from django.shortcuts import redirect
urlpatterns = patterns('',
url(r'^$', redirect('my_app/')),
url(r'^my_project/$', 'my_project.views.homepage'),
)
backyard/my_project/views.py
def homepage(request):
return render_to_response('my_project/index.html', {'data':data})
When I access the page at http:machine-name:8000/ I get an error saying The included urlconf backyard.urls doesn't have any patterns in it I most definitely have URLs in my urls.py, what is the issue?
A URLconf needs to map regexes to views -- the redirect function doesn't return a view; rather, it returns an HttpResponse. You can look at this question for an example of how to define a redirect directly in the URLconf.

Django named routes like Laravel

I'm starting to learn about Django Framework. I have this urls in my urls.py
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
# Examples:
url(r'^hello/$','article.views.hello'),
)
My questions are: Can I give this routes a name, like i would in Laravel? How can i reference those named routes from template?
Thanks!
Yes, you can:
url(r'^hello/$','article.views.hello', name="hello"),
You would reference it in a template as:
{% url 'hello' %}
For more information, including how to give arguments to a named URL, see here.

Django Redirecting passing one (or more) object(s) as argument(s)

Context: I have 3 apps
A, B and C
each one have views.py and urls.py files
views.py
A has do_Ax(request, xpto), do_Ay(request, xpto), do_Az(request, xpto)
B has do_Bx(request), do_By(request), do_Bz(request)
C has do_Cx(request, xpto), do_Cy(request, xpto), do_Cz(request, xpto)
urls.py
A
urlpatterns = patterns('',
url(r'^A_x/$', views.do_Ax, name='A_x'),
url(r'^A_y/$', views.do_Ay, name='A_y'),
url(r'^A_z/$', views.do_Az, name='A_z'),
B
urlpatterns = patterns('',
url(r'^B_x/$', views.do_Bx, name='B_x'),
url(r'^B_y/$', views.do_By, name='B_y'),
url(r'^B_z/$', views.do_Bz, name='B_z'),
C
urlpatterns = patterns('',
url(r'^C_x/$', views.do_Cx, name='C_x'),
url(r'^C_y/$', views.do_Cy, name='C_y'),
url(r'^C_z/$', views.do_Cz, name='C_z'),
the entry point to my global app is through B. There, I check some stuff and want to REDIRECT to A or C view's correct method with the new argument...
Is there a way I can accomplish this?
I've tried a n00b approach but I get a SuspiciousOperation Exception ("Unsafe redirect to URL with scheme ...")
IMPORTANT:
I also a a "parent" app named top which has the following urls.py:
urlpatterns = patterns('',
url(r'^A/', include('apps.top.A.urls', namespace="A")),
url(r'^B/', include('apps.top.B.urls', namespace="B")),
url(r'^C/', include('apps.top.C.urls', namespace="C")),
)
PLEASE NOTE >>>>> XPTO is an object!!!! not a textual value! <<<<<<<<
from django.core.urlresolvers import reverse
from django import shortcuts
def do_Bx(request):
# do some tests here and whatever
xpto = whatever
url = reverse("A_x", args=[xpto])
return shortcuts.redirect(url)
I think you might need to specify the namespace on the reverse() call, since your parent app's urls.py defines them:
As seen here:
url = reverse('A:A_x', args=[xpto])

Django RedirectView and reverse() doesn't work together?

I'm having this weird problem.
When I did this:
from django.core.urlresolvers import reverse
reverse('account-reco-about-you')
# returns '/accounts/recommendations/about-you/'
But when I did this:
# Doesn't Work
recommendations = login_required(RedirectView.as_view(url=reverse('account-reco-about-you')))
# Work
recommendations = login_required(RedirectView.as_view(url='/accounts/recommendations/about-you'))
Error message I get if unrelated. It says my last view is not found, which is there. Any explanation? Meantime, i'll make do with the non-reverse style.
This problem is to do with trying to reverse something at import time before the URLs are ready to be reversed. This is not a problem with RedirectView itself - it would happen with anything where you tried to reverse in your urls.py file, or possibly in a file imported by it.
In the development version of Django, there is a function called reverse_lazy specifically to help in this situation.
If you're using an earlier version of Django, there is a solution here: Reverse Django generic view, post_save_redirect; error 'included urlconf doesnt have any patterns'.
You need to use "reverse_lazy" that is defined in "django.core.urlresolvers" in Django 1.4 and above.
Here is an example urls.py:
from django.conf.urls import patterns, include, url
from django.views.generic import RedirectView
from django.core.urlresolvers import reverse_lazy
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('apps.website.views',
url(r'^$', 'home', name='website_home'),
url(r'^redirect-home/$', RedirectView.as_view(url=reverse_lazy('website_home')),
name='redirect_home'),
)
So in the above example, the url "/redirect-home" will redirect to "/". Hope this helps.
no need for reverse() or reverse_lazy().
simply specify the pattern_name parameter:
RedirectView.as_view(pattern_name='account-reco-about-you')
#wtower
pattern_name will be ok, but you may need to add namespace as below.
RedirectView.as_view(pattern_name='polls:index')

Categories