Why does
View answers
in my template translate to this interpretation by Django:
Request URL: http://127.0.0.1:8000/questions/%7B%%20url%20'answers.views.display_answers'%20Question.id
which of course leads to an url mismatch error.
Seems like its reading in my '{' in ASCII form. Can anyone enlighten me as to why it is so?
EDIT:
This was how i rendered the template--
return render(request, 'display_questions.html', context)
and the template contains the href. My display answer view redirects to another view as such:
def display_answers(request, q_id):
q = get_object_or_404(Question, id=q_id)
ans_list = Answer.objects.filter(question=q)
context = {'question': q, 'ans_list': ans_list}
return redirect('view_answers.html', context)
Error:
The current URL, questions/{% url 'answers.views.display_answers' Question.id, didn't match any of these.
This is correct. If not - your urls.py seems to be wrong. Please post it.
View answers
Edit
Here's a better version of your view.
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response, redirect, get_object_or_404
def display_answers(request, q_id):
q = get_object_or_404(Question, id=q_id)
ans_list = Answer.objects.filter(question=q)
context = {'question': q, 'ans_list': ans_list}
return render_to_response('view_answers.html', context, RequestContext(request))
The problem is your use of redirect in the view. You should be using render or render_to_response unless you actually want to redirect the browser. (Observe using Fiddler, Firebug, or Chrome's developer tools and you'll see that it is redirecting.)
The reason this is not as obvious of a problem is because redirect may take a URL as its first argument. 'view_answers.html' is being interpreted as a relative URL, which may or may not map to a URL in your URLconf. If it does map to a URL, then you get a false positive result that everything appears to be working, but if your web server handles that link instead of Django, then it may just send the template page back in plain text. The solution is, as I said, either render or render_to_response for rendering a page, or redirect with the name of a view or the special name of a URL pattern to redirect to a different view.
Modify your redirect in your display_answers view to use the name of the view, instead of the name of your template (view_answers.html) and don't pass it the context (redirect doesn't take the context as a parameter):
return redirect('your_view_answers_view')
Related
I have this function add_to_cart once a product is added to cart it should redirect to the same.( since the the answers have not solved the issue ).
I have changed at least should redirect to product detail page requires to pass an argument either slug or product id any idea
def add_to_cart(request, **kwargs):
------------------------
------------------------
messages.info(request, "item added to cart")
return redirect(reverse('products:single_product',args=(request)))
url for product detail
url(r'^(?P<slug>.*)/$',single, name="single_product"),
You can do:
return HttpResponseRedirect(request.path_info)
You can also read about how Request/Response works in Django but also about the request.path_info in the docs
You can use request.path to find the current url of the request and then redirect.
from django.shortcuts import redirect
return redirect(request.path)
Send the address where you are calling from in kwargs and redirect to that.
HttpRequest
from django.http import HttpResponseRedirect
return HttpResponseRedirect(request.path_info)
Use "request.path" to get the current url as shown below:
# "views.py"
from django.shortcuts import redirect
def my_view(request):
return redirect(request.path) # Here
Or, use "request.path_info" to get the current url as shown below:
# "views.py"
from django.shortcuts import redirect
def my_view(request):
return redirect(request.path_info) # Here
I have a link that links to a url, like so:
Sign in
This links to a new view in Django, my URL pattern looks like this:
url(r'^sign-in/', sign_in, name="sign_in")
In my sign_in view, I would like to have access to the URL where the user originally clicked the link. Specifically, I would like to add this URL has a parameter called returnUrl to a new URL. My sign_in view can then redirect to this new URL, and the corresponding view will have access to the returnUrl parameter.
For example:
The user clicks 'Sign in'
The website navigates to example.com/sign-in/
The sign_in view redirects to sign_in_page adding the URL from step 1 as a parameter.
sign_in_page has access to the URL parameter.
How can I achieve this in Django? My views look like this...
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.core.urlresolvers import resolve
def sign_in(request):
# I need the original URL the user came from here
# to add it as `returnUrl` below:
return HttpResponseRedirect(reverse('sign_in_page') + '?returnUrl=example.com/whatever')
def sign_in_page(request):
return render(request, 'sign-in.html')
Please let me know if I'm going down a wrong and dark path by using a redirect. To me it seemed like the only way to add a URL parameter - which is important for my implementation. If you know of a way to do it without the redirect - let me know!
If you want to pass data between views, you may use either POST data or GET. In your case GET data is what you want.
So you should use the following URL.
"{% url 'sign_in' %}?returnUrl={{request.path}}"
Then after in your view:
check if GET variable 'returnUrl' was sent
redirect to the next URL
else redirect to the default page
In the following example I am using Class based View inherited from FormView
def get_success_url(self):
if self.request.GET.get('next', ''):
return (self.request.GET.get('next', ''))
return reverse_lazy('experiment_list')
Note: make sure you include the following in your settings, to make sure you get the request.path variable in each template:
## example: settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
# other items...
'django.core.context_processors.request',
# other items...
)
If you want to redirect to referer, Then you can try like this
from django.http import HttpResponseRedirect
def sign_in(request):
# Do your logic here
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
referer = self.request.META.get('HTTP_REFERER')
try this in the view where you need to get the previous url, hope this is what you are looking for
How to I redirect to the same url but append a query_string to it? ie. redirect from "base.com/amt" to "base.com/amt/?ref=blah&id=blah"
urls.py
url(r'^amt/$', RedirectView.as_view(reverse_lazy('amt')), {'SSL':True}),
url(r'^amt/?ref_source=AMT&ref_id=PC', 'core.views.show_amt_page', {'SSL':True}, name='amt'),
in this case I want to go from "^amt/$" to "^amt/?ref_source=AMT..."
Once the redirect is done, I need to execute show_amt_page() int views.py
If there's a better way to do it, can someone explain how that can be done so django executes show_amt_page() in views.py?
The query string is not handled by the url patterns, therefore you can only have one url pattern for /amt/, which will handle requests with and without the get parameters.
url(r'^amt/', 'core.views.show_amt_page', {'SSL':True}, name='amt'),
In the show_amt_page view, you can check for the get parameters in request.GET, and return a redirect response if necessary.
from django.shortcuts import redirect
def show_amt_page(request, SSL):
if 'ref_source' not in request.GET:
return redirect('amt/?ref_source=AMT&ref_id=PC')
# rest of view goes here
...
in urls.py
...
url(r'^$', pages.home, name='home'),
...
Function tu build an urls with GET parameters
from django.core.urlresolvers import reverse
from django.http.response import HttpResponseRedirect
def build_url(*args, **kwargs):
get = kwargs.pop('get', {})
url = reverse(*args, **kwargs)
if get:
url += '?' + urllib.urlencode(get)
return url
Use example
return HttpResponseRedirect(build_url('home', get={'ref_source': 'AMT','ref_id':'PC'}))
return HttpResponseRedirect(build_url('home', get={'opcion': '1'}))
I am trying to redirect to a URL taking user's pk as argument after successful log-in using Django's built-in login view.
Instead of dynamic {{ next }} variable in my login.html I have a generic landing view of logged-in users;
<input type="submit" value="login" />
<input type="hidden" name="next" value="{% url 'userredirect' %}" />
In my urls.py I have;
url(r'^users/', views.users, name='userredirect'),
url(r'^(?P<pk>\d+)/', UserHome.as_view(), name='userhome'),
and in my views.py I have
#login_required
def users(request):
url = reverse('userhome', kwargs={'pk':request.user.id})
return HttpResponseRedirect(url)
What I am doing here is redirect to a detail view that I have named UserHome on the user model after successful login using 2 redirects as I do not know of a way to redirect to UserHome directly (it takes user's pk as argument). It works and I indeed get redirected to the user's homepage when checking via the browser.
Reference;
The "next" parameter, redirect, django.contrib.auth.login
But when running the below test
def test_page_redirects_to_user_home_on_login(self):
"""
Test to assure that the login page redirects to the user's
home page
"""
username = "someusername"
password = "somepassword"
user = User.objects.create_user(username=username,
password=password)
user.save()
response = self.client.post(reverse("userlogin"),
{"username":username,
"password":password},
follow=True)
assert response.path == self.client.get(reverse("userhome",
kwargs={"pk":user.id}
)
)
I get the below failure
AttributeError: 'HttpResponseNotFound' object has no attribute 'path'
It seems the test client gets no page. Would it be that I am using the userredirect view simply for redirecting and the client do not go ahead and get the UserHome class view to its context.
I'm a newbie to Django/Python. Someone please sort this out for me :).
I look forward either to a way where I can redirect directly from the template for login view to UserHome or a way to rewrite my test.
Hard to say without much more insight in your project. Here are a few possibilities and such.
Response has no path
response indeed has no path, you probably wanted this:
assert response.wsgi_request.path == reverse("userhome", kwargs={"pk":user.id})
Include next in your test
You're simulating data from the login form, but you're omitting the next field.
Add it to the POSTed data:
{"username":username,
"password":password,
"next": '/users/',}
Take a look what's in the response
It might help to see what's in the response in your test. For example:
print(response.redirect_chain)
Perhaps you're not even reaching the login page?
Are you missing LOGIN_URL in your settings.py?
LOGIN_URL = '/login/'
Without it, you'll be redirected to '/accounts/login/', which might be the 404 you're seeing.
Finaly - why? :)
Perhaps you have some special use case, but I'd usually read user's id (a.k.a. pk) from request.user. That way I (for example) can't access example.com/<your_id> and access your homepage. Of course, that might be just what you intend. In that case I'd still have a separate URL for current user, it will probably pay off later. Something like this:
...
url(r'^/', UserHome.as_view(), name='userhome'),
url(r'^(?P<pk>\d+)/', UserHome.as_view(), name='userhome'),
...)
class UserHome(DetailView): # also protect with some LoginRequiredMixin
model = User
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
id = self.kwargs.get('pk', self.request.user.id)
return queryset.filter(id=id).get()
First things first: The error you get is because the line
response = self.client.post(reverse("userlogin"),
{"username":username,
"password":password},
follow=True)
raises a 404 error, hence resonse is a HttpResponseNotFound.
Before testing anything else is it a good practice to first test that your request was successful. Something along the line of:
self.assertEqual(response.status_code, 200)
Also, you are hard-coding url's which goes against DRY and is often the source for trouble (maybe it is the case here).
It would be better to name all your urls:
url(r'^users/', views.users, name='user_redirect'),
and then use this in your template
<input type="hidden" name="next" value="{% url 'user_redirect' %}" />
and this in your view
from django.core.urlresolvers import reverse
#login_required
def users(request):
url = reverse('userhome', kwargs={'pk': request.user.id})
return HttpResponseRedirect(url)
And finally, you are taking an unnecessary step with the redirect. Assuming UserHome is a DetailView on User, you could have this code:
##urls.py
url(r'^users/', UserHome.as_view(), name='userhome')
##views.py
from django.views.generic import DetailView
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
class UserHome(DetailView):
model = User
def get_object(self, queryset=None):
return self.request.user
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(UserHome, self).disatch(*args, **kwargs)
This would also ensure that no user accesses another user's "userhome".
Doing all this should help you find what went wrong with your code. Good luck!
I'm trying to have my form submit to a route which will validate the data then redirect back to the original route.
For example:
User loads the page website.com/post
Form POSTs the data to website.com/post-save
User gets redirected back to website.com/post
Pyramid is giving me some troubles doing this.
Here's my slimmed down views.py
def _get_link_form(post_data):
""" Returns the initialised form object """
return LinkForm(post_data)
def home_page(request):
form = _get_link_form(request.POST)
return {'form' : form}
def save_post(request):
""" form data is submitted here """"
form = _get_link_form(request.POST)
if not form.validate():
return home_page(request, form)
This is the code I've been playing around with. Not only does it not work, it also feels messy and hacked up. Surely there's a simpler way to 'redirect after-POST' in Pyramid?
Your problem is most easily solved by simply POSTing to the same URL that your form is shown at, and simply redirecting the user away from the page when the POST is successful. That way until the form is successfully submitted you do not change URLs.
If you're just dying to POST to a different URL, then you need to save the data using sessions, since you're obviously handling the form data between requests.
Typically if you want to be able to handle errors in your forms you would use a session and flash messages. To do this you simply add a location for flash messages to appear in your base template and setup session support using something like pyramid_beaker.
Assuming your home page is setup at the 'home' named-route:
from pyramid.httpexceptions import HTTPFound
def myview(request):
user = '<default user field value>'
if 'submit' in request.POST:
user = request.POST.get('user')
# validate your form data
if <form validates successfully>:
request.session.flash('Form was submitted successfully.')
url = request.route_url('home')
return HTTPFound(location=url)
return {
# globals for rendering your form
'user': user,
}
Notice how if the form fails to validate you use the same code you did to render the form originally, and only if it is successful do you redirect. This format can also handle populating the form with the values used in the submission, and default values.
You can loop through the flash messages in your template of choice using request.session.peek_flash() and request.session.pop_flash().
route_url supports mutating the query string on the generated url as well, if you want to flag your home page view to check the session data.
You can obviously just pass everything in the query string back to the home page, but that's a pretty big security vulnerability that sessions can help protect against.
The Pyramid documentation has a particularly on-point section with the following example:
from pyramid.httpexceptions import HTTPFound
def myview(request):
return HTTPFound(location='http://example.com')
I do this like so:
from pyramid.httpexceptions import HTTPCreated
response = HTTPCreated()
response.location = self.request.resource_url( newResource )
return response
This sends the HTTP Created code , 201
The Pyramid documentation has content about Redirect, you can see more information in below link :
Pyramid documentation
import pyramid.httpexceptions as exc
raise exc.HTTPFound(request.route_url("section1")) # Redirect
Edited:
Actually you can do that on client side with Javascript, first you should send particular response to client side(either with flashes some data or return Response object):
window.location = '{{ request.route_path("route_name") }}';
Assuming your homepage is the default view of your pyramid web app, you can do:
def _get_link_form(post_data):
""" Returns the initialised form object """
return LinkForm(post_data)
def home_page(request):
form = _get_link_form(request.POST)
return {'form' : form}
def save_post(request):
form = _get_link_form(request.POST)
if not form.validate():
from pyramid.httpexceptions import HTTPFound
return HTTPFound(location=request.application_url)
Basically you need to know how the home_page view was "added" to your Configurator. If your homepage actually lives at /few/levels/deep/homepage then a redirect might look like this:
return HTTPFound(location=request.application_url + '/few/levels/deep/homepage')
A clean way is using the "overload" provided by pyramid for different request types, por example, you can decorate your methods this way:
#action(request_method='GET',
renderer='mypackage:/templates/save.mako',
name='save')
def save(request):
''' Fill the template with default values or leave it blank'''
return {}
#action(request_method='POST',
renderer='mypackage:/templates/save.mako',
name='save')
def save_post(request):
""" form data is submitted here """"
# process form
In the HTML, you must call the action form, like
<form method="POST" id="tform" action="${request.route_url('home', action='save')}">
This way, one method is processed when the method POST is used, and the other when the GET is used. The same name, but two implementations.