I'd like to do that, but this
return HttpResponseRedirect(request.path_info)
yields a redirect loop. I suspect it's because the request object is the website you're on at that time.
Whole function
def permit(request, pk):
if int(request.user.id) == int(pk):
return HttpResponseRedirect(request.path_info)
else:
return render_to_response('forbidden.html')
The user has clicked a link on a webpage. pk is the regx search term in urls.py. I want this to redirect to the page the user clicked on if the user is authorized, not the page he is currently on.
The problem is that as soon as the user is "redirected", the function permit is called. The if statement is true (since it is the authenticated user) and the redirect happens again... To prevent this loop, only redirect, if there was a post request:
def permit(request, pk):
if request.POST:
if int(request.user.id) == int(pk):
return HttpResponseRedirect(request.path_info)
else:
return render_to_response('forbidden.html')
## Return your login page here.
Related
This is my first web development project. It is basically an e-voting website. The index page is the login page. So, when the user login using valid credentials, the website will take the user to the voting page where that contains a voting form and some other options like feedback, news updates, etc. in the Navigation bar. The login request is "POST". The vote submission request is also "POST". Here, if the user presses other option like submitting feedback or viewing the candidates, will take the user to that respective pages. And if the user presses the back button, the website should take him to the voting page again where he can again use other facilities voting, viewing candidates, or submit feedback. But the problem I face is if the user presses the back button from the 3rd page i.e., feedback page or viewing candidates page, it shows an error "Confirm Form Resubmission". Help me fix this bug. Thank You in advance.
views.py
from django.shortcuts import render,redirect
from django.contrib.auth.models import User, auth
from django.contrib import messages
from .models import voteoption,receive,received_feedback,updates
# Create your views here.
def index(request):
return render(request,"index.html")
def login(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username,password=password)
if user is not None:
auth.login(request, user)
cand = voteoption.objects.all()
return render(request,"votepage.html", {'candidates': cand})
else:
messages.info(request,'Invalid Email or Password')
return render(request,'index.html')
else:
return render(request,'index.html')
def logout(request):
auth.logout(request)
return redirect("/")
def update(request):
news = updates.objects.all()
return render(request,'updates.html', {'upd': news})
def contact(request):
return render(request,'contact.html')
def feedback(request):
return render(request,'feedback.html')
def feedbacksubmit(request):
feedback = request.POST['feedback']
data = received_feedback(response=feedback)
data.save()
messages.info(request,'Feedback Submitted Successfully')
return render(request,'submitted.html')
def candidates(request):
cand = voteoption.objects.all()
return render(request,'candidates.html',{'candidates': cand} )
def submitvote(request):
reg_no = request.POST['reg_no']
selection = request.POST['selection']
voter = request.POST['voter']
if receive.objects.filter(mail=voter).exists():
messages.info(request,'You have already Voted')
return render(request,'submitted.html')
else:
data = receive(reg_no=reg_no,selection=selection,mail=voter)
data.save()
messages.info(request,'You have voted Successfully. Thank You!')
return render(request,'submitted.html')
What changes should I make?
Since this is my first project, there might be a lot of poor coding techniques. So, please suggest me with better codes where I should replace in my code even though if it is not related to this bug.
You will want to handle successful form submissions with the Post/Redirect/Get pattern to prevent the back button from returning to the previous post. In your Django project this is done by returning a redirect from the view instead of an HttpResponse. Redirect docs
from django.shortcuts import redirect
def view_function(request, ...):
... # code processing the POST
if post_request_successful:
return redirect(path)
else:
return HttpResponse(...) # or render(...) or whatever you usually return
Where path is a url. Most likely you want to either use the same path in the request request.path or get a path by reversing a url name from your urls.py file (from django.shortcuts import reverse). Reverse docs
Is it possible to have 2 redirect() in the same django view. so when the like button is in the home page, i want it to redirect back to home page, if like button is in detail page, i want to redirect back to detail page?
For instance:
def LikeView(request, slug):
context = {}
post = get_object_or_404(BlogPost, slug=slug)
post.likes.add(request.user)
if in homepage:
return redirect('HomeFeed:detail', slug=slug)
else:
return redirect('HomeFeed:main')
def delete_blog_view(request,slug):
context = {}
user = request.user
#YOU WANT to check if user is authenticated. if not, you need to authenticate! redirect you to that page
if not user.is_authenticated:
return redirect("must_authenticate")
account = Account.objects.get(pk=user_id)
blog_post = get_object_or_404(BlogPost, slug=slug)
blog_post.delete()
return redirect(request.META.get('HTTP_REFERER', 'account:view', kwargs={'user_id': account.pk }))
Pass the redirect URL in a next URL param. Like so:
<!-- In homepage template -->
Like
<!-- in Detail template -->
Like
or simply:
Like
To always pass the current URL as the redirect URL.
And then in your LikeView:
def LikeView(request, slug):
...
next = request.GET.get("next", None)
if next and next != '':
return HttpResponseRedirect(redirect_to=next)
# Then have a default redirect URL in case `next` wasn't passed in URL (Home for Example):
return HttpResponseRedirect(redirect_to="/home")
As mentioned in the Django Docs, this isn't safe (for most apps), so you have to check if URL is safe then redirect to next otherwise just return a default safe in-app URL.
Read on the url_has_allowed_host_and_scheme function to check if URL is safe on this Docs page
Suppose I have a view like this:
def home(request, redir_url, template = 'example/home.html')
if request.session["profile_name"] and request.session["token"]:
return HttpResponseRedirect(redir_url)
if request.POST:
driver = Facebook()
res = driver.RetLoginUrl(redir_url)
return HttpResponseRedirect(res)
return render(request, template)
In this view, first I check the session variables, if the user has already logged in, I redirect to the welcome page, and if not I have a login button with which the user can authorize my app with Facebook.
And the template has a form method=POST with the login button as input.
My question is, how does the if statement (if request.POST) get executed, when there is a statement return render(request, template) at the end of the view. After the page is rendered, i.e. return render() statement has been executed, shouldn't the view function terminate, hence without a form response being submitted to the view?
Basically, I just would like to understand the execution flow of a django view. Is the return render() statement executed first, and then waits for user input?
It should be
if request.method == 'POST'
This sentence is executed every time you access the url configured for this view. If the method of the request was POST, when the user presses the button, then the code inside if is executed and a HttpResponse is returned
In the example the line render(request, template) is only executed when the method was'nt POST (maybe GET, PUT, DELETE, etc).
Finally, you could use Django Login Decorator to avoid the session variables checking
As is clear from this question and the accepted answer, the only way to redirect with a context is to save it in the session. Why doesn't Django have a simple redirect method that takes a context object like render does?
For e.g., let us say, I have a login view that accepts both GET and POST requests. GET request simply renders the form in which a user can enter credentials, which will trigger a POST call to the same view.
def login(request, *args, **kwargs):
if request.method == 'GET':
context = {}
context.update(csrf(request))
return render(request, 'login.html', context=context)
elif request.method == 'POST':
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=username, password=password)
if user is not None:
auth.login(request, user)
return redirect('loggedin')
else:
# Redirect with a context object that has 'error':True,
# so that the login template can display error message:
# 'Username/password is incorrect'
redirect('login')
return redirect('invalid')
Please notice that upon the entry of wrong combination of username/password, I would like to redirect the user to the same login page with error variable set in the context so that the template can draw out a warning.
I know, a possible answer would be to invoke render with custom context than redirect, but then isn't is a good practice to always use redirect after a post request?
Thanks.
This is not in any way a limitation of Django, and you don't seem to have thought through how this would even work.
A redirect is a simple HTTP response that tells your browser to go and request another page. How would Django, or any framework, pass a context through that redirect? Where would it go? And the receiving URL would have its own view, which would generate its own context - what would it do with the "redirect" context? How would it know?
But in any case I don't understand why you would want to redirect back to the same page on an error. Simply redisplay it. The redirect-after-POST principle is for successful posts, not unsuccessful ones.
I have a protected view in my app which just accepts POST requests.
#app.route("/booking", methods=("POST", ))
#login_required
def booking():
arg1 = request.form.get("arg1")
arg2 = request.form.get("arg2")
When an unauthorized user tries to access this view, I want them to
login and then be redirected here.
Right now, my login view looks like this:
#app.route("/login", methods=("GET", "POST"))
#login_required
def login():
do_login()
return redirect(request.args.get('next') or url_for('home'))
So what ends up happening is a POST request to /booking (which is the
"next" parameter) and I get a NOT ALLOWED error.
The problem is that login() makes a GET request to booking(). I can
get around that, but I am not sure how to retrieve the original POST
form arguments from /booking? Any ideas to get round that?
I would solve this by pulling the data and putting it in the session. You can remove the #login_required decorator and check this in the function using current_user.is_authorized. See Flask Sessions and Flask Login.
Something like this might work for you, I didn't test it:
from flask import session
from flask_login import current_user
#app.route("/booking", methods=("POST", ))
def booking():
if not 'arg1' in session.keys() and not 'arg2' in session.keys():
session['arg1'] = request.form.get("arg1")
session['arg2'] = request.form.get("arg2")
# Now the data will persist in the session
if current_user.is_authorized:
# Do what you need...
else:
# Redirect to login, session will persist
Why would you only use POST in the booking view ? You are probably rendering a form which should also allow GET.
#app.route("/booking", methods=['GET','POST'])
#login_required
def booking():
# render the form. something like
form = BookingForm()
# Check if POST
if request.method == 'POST':
# process the form now and do whatever you need.
return redirect(url_for('index'))
# code below will run if not POST. You should render the template here
return render_templte('booking.html')