Django HttpResponseRedirect and reverse() - django

Situation in short. For some reason reverse() method does not work.
in PROJECT urls.py
url(r'^enrollment/', include('project.enrollment.urls')),
in APP urls.py
url(r'^thanks/$', 'enrollment.views.thanks', name='enroll_thanks'),
and in views.py
from django.core.urlresolvers import reverse
def thanks(request):
return render_to_response('enrollment/thanks.html', {}, context_instance=RequestContext(request))
def enroll(request):
''' some code for handling the form'''
return HttpResponseRedirect(reverse('enrollment.views.thanks'))
This reverse causes following error:
Could not import project.views. Error was: No module named views
in file ../django/core/urlresolvers.py in _get_callback, line 167
Any idea why this does not work? Next step is to call the thanks-view with a parameter, but that should be easy after this setup works. Should there be something more to be imported in views.py?

From the docs for reverse: "As part of working out which URL names map to which patterns, the reverse() function has to import all of your URLconf files and examine the name of each view. This involves importing each view function. If there are any errors whilst importing any of your view functions, it will cause reverse() to raise an error, even if that view function is not the one you are trying to reverse."
Are any of your urls referencing project.views....?

Not too sure of your module layout but based on your include() line in urls.py it looks like you may want to add "project." to the argument in your reverse() call as well.
reverse('project.enrollment.views.thanks')
That or you may have a bug in the view.

Related

Django: cannot import name 'url_patterns'

I have a weird edge-case where I need to use the data stored in urls.py within a view, but since the urls.py file imports the view, I am getting a circular import error when I try to import the url data into the view.
cannot import name 'url_patterns'
Does Django have any way of grabbing all the url patterns within a view that could circumvent this error? The fact that the reverse() function exists indicates that it might.
You can solve this typically (well it depends a bit), by importing the urls in your view(s) locally. Like:
# urls.py
import someapp.views
url_patterns = [
url('some_url/', someapp.views.some_view),
]
and then import the urls in the views like:
# views.py
def some_view(request):
from someapp.urls import url_patterns
# ...
# do something with urlpatterns
# return response
pass
We here thus postpone the importing of the urls.py in the views, until the view function is actually called. By that time both the views.py and urls.py are (usually) already loaded. You can however not call the view in the views.py directly, since then the import is done before the urls.py is loaded.
Note that usually you do not have to import url_patterns, and you can use reverse(..) etc. to obtain a URL that corresponds to a view function (even passing parameters into a dictionary, etc.). So I really advice to look for Django tooling to handle URLs.
Found an answer:
from django.urls import get_resolver
url_patterns = set(v[1] for k,v in get_resolver(None).reverse_dict.items())

Why do I get RemovedInDjango110Warning when redirecting to log in page lite this?

For my index page I want to show one page if the user is logged in and redirect to the login page if the user is not logged in. I do this with the following view code:
from __future__ import unicode_literals
from django.shortcuts import render_to_response, redirect
from django.contrib.auth.views import login
def index(request):
if not request.user is None and request.user.is_authenticated():
return render_to_response('foo/index.html')
else :
return redirect(login)
However I get the following warning:
RemovedInDjango110Warning: Reversing by dotted path is deprecated (django.contrib.auth.views.login)
All I have found on the topic is this question but although it seems vaguely related I can't understand how to apply it to my problem.
What does "Reversing by dotted path" mean? And where do I do it and how should it be done instead?
EDIT: I thought those lines where the problem but seems to not be the case. So the question is still similar but more general:
What does "Reversing by dotted path" mean? How do I figure out where I do it? And what should be done instead?
I agree that the 'Reversing by dotted path is deprecated' message is a bit confusing in this case.
Say you have:
url(r'^login/$', views.login, name='login-page'),
The deprecation warning is saying that you should use the url name 'login-page' instead of the dotted path, 'django.contrib.auth.views.login' when you reverse urls.
In your case, you are reversing urls when you use the redirect shortcut. When you do
return redirect(login)
the callable login is converted to a string 'django.contrib.auth.views.login' and the string is reversed, triggering the warning.
You can stop the warning by changing that line to:
return redirect('login-page')
You may then be able to remove from django.contrib.auth.views import login if it is not used anywhere else.
Note that the idiomatic way to write your view would be to use login_required and render:
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
#login_required
def index(request):
return render(request, 'foo/index.html')

When using i18n_patterns, how to reverse url without language code

I am using i18n_patterns but I want to use reverse to create a link to the page without language in the url (such that the user will be redirected based on cookies and headers and such).
I have tried
from django.utils.translation import activate, deactivate, get_language
current_lang = get_language()
deactivate()
url = reverse(things)
activate(current_lang)
That works for getting other language versions using activate(target_lang), but if I deactivate I just get urls for the default language (/en/account/ but I want /account/).
I already thought getting alternate language versions is overly complicated, but this I cannot manage at all. Any hints? (Without manually stripping LANGUAGE_CODE from the url)
UPDATE: I also tried
from django.core.urlresolvers import get_resolver
get_resolver(None).reverse(*args, **kwargs)
but get NoReverseMatch
I think the easiest way is to let Django resolve the URL with the language prefix and then just remove the language prefix.
You can write the following function:
import re
from django.core.urlresolvers import reverse
def reverse_no_i18n(viewname, *args, **kwargs):
result = reverse(viewname, *args, **kwargs)
m = re.match(r'(/[^/]*)(/.*$)', result)
return m.groups()[1]
Now, anywhere in your code you can do something like this:
from myproject.utils import reverse_no_i18n
def my_view(request):
return HttpResponseRedirect(reverse_no_i18n('my_view_name'))
You might also want to create a custom {% url %} templatetag which calls your custom function.
I also spent time to find a nice solution and here is mine.
Next to main urls file ('my_project/urls.py'), create the file 'my_project/urls_without_lang.py' with the content below.
Then, you can use reverse('viewname', urlconf='my_project.urls_without_lang')
Django=<1.11
from copy import copy
from django.urls.resolvers import LocaleRegexURLResolver
from .urls import urlpatterns as urlpatterns_i18n
"""
Purpose of this file is to be able to reverse URL patterns without language prefix.
This is usefull to build URL meant to be communicated "outside" of the domain without any language duty.
To use it with 'reverse' method (from django.shortcuts module), simply give the additional parameter:
`urlconf='my_project.urls_without_lang'`
Example: `reverse('viewname', urlconf='my_project.urls_without_lang')`
"""
urlpatterns = copy(urlpatterns_i18n)
for el in urlpatterns_i18n:
if isinstance(el, LocaleRegexURLResolver):
urlpatterns.remove(el)
urlpatterns += el.url_patterns
Django>1.11
from copy import copy
from django.urls import URLResolver
from .urls import urlpatterns as urlpatterns_i18n
urlpatterns = copy(urlpatterns_i18n)
for el in urlpatterns_i18n:
if isinstance(el, URLResolver) and isinstance(el.urlconf_name, list):
urlpatterns.remove(el)
urlpatterns += el.url_patterns
Hope that will help some of you.

return redirect in django 1.5 throwing import exception

I have simple code that basically looks like this:
from django.shortcuts import redirect
def my_view(request):
... does stuff
return redirect('/some_other_url/')
The exception being thrown is "Could not import django.views.generic.simple.redirect_to. Parent module django.views.generic.simple does not exist." When I comment out the "return redirect" code (and replace with return HttpResponse("")) I no longer get the error.
stack trace: NO Longer availalbe because it was posted outside SO http://dpaste.com/1007500/
I just upgraded from django 1.3.1 to django 1.5. Checking the documentation for 1.5 it looks like I should still be able to use "redirect()". I could not find any answers to this from several google searches so hopefully its not some blind oversight on my part.
Looks like problem not in django.shortcuts.redirect, but in view, that process url you were redirected to. According to your traceback, view, that process url 127.0.0.1:8000/post_station/ use somewhere django.views.generic.simple.redirect_to. In django 1.5 you problably shouldn't do it. Use django.views.generic.RedirectView insead.
You can find answers here "No module named simple" error in Django
from django.shortcuts import redirect
def my_view(request):
... does stuff
return redirect('/your_app/some_other_url/')

Django can't find HTML file

I have a html file ('search.html') with a form on it. I have saved it to ~/Django/Templates just for the sake of argument. The Django book said it doesn't matter where I save it, because the framework will find it. Anyway, I have set up a function in the views.py file to render this file. Here it is:
from django.http import HttpResponse
from django.shortcuts import render_to_response
def search(request):
return render_to_response('search.html')
I have this function called in the urls.py file as well:
urlpatterns = patterns('',
(r'^$', index),
(r'^search/$', search),
However, whenever I go to visit the page with the ~/search in the URL, I get the following:
TemplateDoesNotExist at /search/
What's the problem?
In your settings.py file there is a line...
TEMPLATE_DIRS = (...)
You want to make sure the directory containing the template is in that tuple.