When I hit refresh on a page with flash message already showing, the message persist even after the refresh. How do I stop the message from showing on refresh? I noticed when I use render_template after flash the flash message will persist, but when I use redirect it doesn't. However I have to pass a non-3xx status code when I flash a message, using redirect only gets user wait on a redirection page until further action, which is not desirable either.
Method 1: Shows the right page but message persists on refresh:
flash("An error occurred.")
return render_template("page.html"), 400
Method 2: Stuck on redirection page but message doesn't persist on refresh:
flash("An error occurred.")
return redirect(url_for('show-page'), code=400)
Redirecting...
You should be redirected automatically to target URL: /page. If not click the link.
Method 2 does work as I intended if I pass a 3xx code, but I need to pass non-3xx code.
Note: The user should be on the same page throughout.
In my case, the flash message pops up to give users feedback immediately after they submit a form. The message persisted after refresh because the refresh triggered a double submit of the form. Redirect prevents the double submit problem and subsequently the persistent message. I read another post on the status code. Since this is all user-facing rather than a REST API, status code doesn't really matter, I went with method 2 with a 3xx default status code:
flash("An error occurred.")
return redirect(url_for('show-page'))
In my case all the messages were stuck in session when using the stream_template and when changing to render_template the messages were removed from the session normally.
#app.route("/startpage", methods=['GET', 'POST'])
def startpage():
if validate_token() == True:
flashes = session.get('_flashes', [])
print(flashes)
#return stream_template('start_page.html', title="Inicio")
return render_template('start_page.html', title="Inicio")
else:
return redirect(url_for('index'))
I did some tests and in my case it really was that. I didn't find anything in the Flask documentation talking about this difference in stream_template and render_template.
Related
Tell me with what you can wait for a response to another endpoint?
I am on the main page (index), entering something into the form. The POST request is sent to another server. At this moment:
another server processes the data and, depending on their correctness, makes a POST request to my url /answer (True or False).
I will be redirected, for example, to another page.
How to register the logic of another page (another) so that Django waits for a POST request from another server to /answer and depending on this request True/False, I output everything OK or everything Bad on this page?
url.py
urlpatterns = [
path('index/', index, name='index'),
path('page_2/', page_2, name='page_2'),
path('answer/', answer, name='answer'),
]
-------------------------------------------------
views.py
def index(request):
requests.post(example.com, data='My data')
return redirect('page_2')
def page_2(request):
# wait request in answer
if request.session['answer'] is True:
return 'Ok'
retunr 'Bad'
def answer(request):
data = request.data
# send to page_2 or save in request.session['answer']
return Response(status=200)
I reckon it's a strange situation and it's better if you could redesign the logic of your code so that the view functions process the request ASAP and not busily wait for external events to be triggered as it increases response time.
However, in order to achieve this purpose we need a communication channel between index and answer view. So to implement a communication like this:
index: Hey answer! I've sent the request. I'm going to sleep, wake me up if you got its result.
answer: Oh I got it man. Here you are. Wake up!
index: Thanks. Now I process it and return my response.
So this channel might be anything! A model in database, some entities in redis, some files in filesystem, etc.
One possible solution using the models might be:
Create a model(name it ExampleRequest for example) consisting of a boolean field named received
In index view, create an instance of ExampleRequest with received = False before sending the request.
In answer view, find the previously created ExampleRequest and set its received field to True
In index view, after sending the request, in a while loop, query the database and check if the created ExampleRequest instance has received = True? If yes, then the external server has called answer. So break the while and do the rest of the work; otherwise, just time.sleep(1) and continue the while loop.
Just note:
When multiple clients are using your website, some of them might request index view and then there will be more than one instance of ExampleRequest. In answer view, you have to be able to find out the current request is related to which one of those instances. You might need to store a unique data related to that request in ExampleRequest model.
You might consider the situation where the other server doesn't call answer view ever. So there might be an upper bound for the iterations of index view's while loop.
Also you may remove ExampleRequest instances after capturing them in index view in order to optimize disk usage of your database.
I say it again, it's better if you can do the polling stuff in frontend instead of backend to avoid high response time and other syncing issues.
This might not the complete answer, but it gives you way.
def index(request):
requests.post(example.com, data='My data')
return redirect('page_2')
Change it to following
import httpx
async def index(request):
async with httpx.AsyncClient() as client:
response = await client.post(example.com, data='My data')
print(response.json())
I have pushed my django project onto pythonanywhere, a free hosting website.
The code works fine in my local and onto the web as well, but somehow have an issue with instagram authentication.
Instagram uses an oauth2 authentication system, in which when the user allows your application, it redirects the user to a specified URI and appends the code inside the URL which further can be used to authenticate the user.
Instagram doesn't allow localhosts as valid redirect URL, therefore for the testing I have used something like this
InstaAuth.html
<script>
history.pushState(null, null, '?code=AQD_4...q6#_');
function reloadThePage() {
window.location.reload();
}
</script>
<button type="submit" class="btn btn-primary" onclick="reloadThePage()">Continue</button>
which changes the state of the url but doesn't actually refresh it. Then I have button which refresh the current page, which directly trigger below code in my views.py and gets the code through request.GET['code'].
Views.py
def instaAuth(request):
if 'code' in dict(request.GET).keys():
code = request.GET['code']
print('the given code is', code)
core = instaCore()
user_id, short_lived_access_token = core.code_to_short_access_with_userid(code_string=code)
print(user_id)
obj = InstaModel.objects.get(username=request.user)
obj.instagram_userid = user_id
obj.short_lived_access_token = short_lived_access_token
obj.is_active = True
obj.save()
print('all data saved!')
messages.success(request, "Your instagram is connected!")
return render(request, 'authentication/success.html')
return render(request, 'authentication/instaAuth.html')
Above code works perfectly fine when I add the code using pushState mehod and refresh the page using button. but when I do the same in my webapp, authorising the app and then clicking continue button, it throughs the KeyError at /instaAuth 'access_token'.
This error usually occurs when someone tries to use the same oauth code more than once. When I looked into the logs I found the error in the same line where I was exchanging the oauth code with the access token. I tried to look into the network tab of the requests, but I am not sure what is missing. I have done similar thing using streamlit and it worked fine, you can check the streamlit app here https://instalogintest.herokuapp.com/
I am stuck at this place, I want to have some logic which either doesn't refresh the page or a way that django knows that this request is coming from Instagram and verify the user using the code.
Problem solved after going through everything for a whole day. Can't believe it was such a small mistake.
I didn't change the redirect uri when I was exchanging the code with access_token.
The problem was I was not printing the error, it always through try and error block, will never do the same again.
Always use this syntax
try
do_something()
except Exception as e:
print(e)
do_something_else()
When i do this, it work:
return redirect('/admin-login')
But when I try to pass a http code in parameter, the redirection dont work and I get to this page:`
return redirect('/admin-login', code=401)
Redirecting...
You should be redirected automatically to target URL: /admin-login. If
not click the link.
How do I fix this? Thanks
The message is actually there in both cases. Flask uses the Location header field to trigger a redirect. So when your browser sees that it skips displaying the content and immediately redirects.
The reason it doesn't work for code=401, is because 401 Unauthorized is a client error. This results in the browser ignoring the redirect (at least with Chrome).
The Flask documentation for flask.redirect mentions the following:
Supported codes are 301, 302, 303, 305, and 307.
It doesn't specifically mention why they are the only supported ones. But the best assumption is because browsers generally shouldn't redirect if it's not a 3XX status code (Redirection).
The solution is thus to use one of the supported status codes.
Solution that worked for me:
#app.errorhandler(401)
def page_not_found(e):
return redirect('http://example.com')
previously I had been trying:
#app.errorhandler(401)
def page_not_found(e):
return redirect('http://example.com', code=401)
...but it turns out the ", code=401" specification is not needed if it's up in errorhandler, and without it, it automatically redirects.
my solution was to create custom error pages, adding the redirect meta to the header. This will force the page to be redirected to the homepage after 5 seconds of the page being open =}
<meta http-equiv="refresh" content="5;url=/" />
This is the login dashboard that appears after successful login . When I clicked on logout it goes to the following view config where I have cleared the session and request.
LOGOUT CODE HERE
#view_config(route_name='logout',effective_principals=Authenticated,renderer='templates/index.jinja2')
def logout(request):
DBSession.query(User).filter(User.id==request.user.id).update({"verified":'N'}) #making the verified 'N' again
session=request.session
headers=forget(request)
session.invalidate() #deleting the session
return HTTPFound(location=request.route_url('home'),headers=headers)
The problem is
when I click on the back button of the browser it goes back to the same link as in the picture. Is it because my session is not being cleared or what mistake is being here.
Try adding http_cache=0 parameter to the view config decorator for the dashboard:
#view_config(route_name='dashboard', ...,http_cache=0)
Most likely the page comes from the browser cache. There is nothing you can do here as this is intended behavior of web browsers. There is only problem if the page comes up if you directly type it in the address bar.
I'm writing an app using Flask.
I have a set of routes and they work.
What I want to do on the client side is to ignore any requests to invalid URLs. That is I do not want to render any 404/error pages in the app. I would like an alert that says the URL is invalid and for the browser to simply stay on the same page.
I don't want to be checking the URLs in JavaScript on the client, as this would expose them.
I have a route which responds correctly to unknown URLs:
#app.errorhandler(404)
def non_existant_route(error):
return jsonify({"no":"such page"})
If I delete the return statement I get a 500 error.
I can't use abort()
Does this idea violate some HTTP principle?
Thanks
It sounds like you need a "catch-all" endpoint. Typically, it seems a catch-all endpoint would return a generic 404, but in your case, you probably want to return a 200 with some contextual information. Here's basically how you can do it (credit goes to http://flask.pocoo.org/snippets/57/):
from flask import Flask
app = Flask(__name__)
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def catch_all(path):
# returns a 200 (not a 404) with the following contents:
return 'your custom error content\n'
# This is just one of your other valid routes:
#app.route('/stuff')
def stuff():
return 'stuff\n'
if __name__ == '__main__':
app.run()
If you run this and curl various endpoints of the test app, here's what you get:
$ curl localhost:5000/stuff
stuff
$ curl localhost:5000/foo/bar
your custom error content
$ curl localhost:5000/otherstuff
your custom error content
As you can see, your other routes will still work as you expect.
I've decided a solution to this is too hard! I can not find any way to get the browser to ignore a response. There is no response header for 'do nothing'. If there was we would probably never see a webserver error again, which would not be good.
I could ajaxify all the requests as a way to grab the response headers and analyze them before any rendering or redirecting happens. That starts to break all the navigation (back buttons at least) and the pretty URLs. I could bung in a JS routing framework etc, and while I'm leaning how it works I'm not building my app (I already have enough to learn!)
#app.errorhandler(404)
def page_not_found(error):
return redirect(url_for('index'))
If you come up with something great post it anyway, I'm not the first to ask this question, and probably not the last.
Thanks
I remember reading about a javascript library some days ago (but I don't remember the name...). The clou with this library was, that all links and form submits were loaded not directly into the browser "_top" frame/window but into a hidden div and afterwards, when done, the content of the page was replaced by the content of this hidden div.
So if you want to catch bad links and such on client side you could hook up all links and submits and check the http response code. If it is not 200 (ok) you display an error. If it is okay you decide, if you replace the old page with the new content.
But there are two problems with this solution:
1. You would have to change the browsers location (in the address bar) without reloading the page of course!
2. It might get tricky to post some file uploads with javascript.
If I find the link or name of the js-library I saw, I will tell you!