How do I create an alert based on the last page visited? - python-3.x

I'm trying to mimic the 'dashboard' of https://finance.cs50.net/ (once you've logged in), which displays a 'bought' alert if you are redirected there from /buy, but no alert if you end up there otherwise.
I'm using Flask, jinja2, python, and HTML. The address provided is ("/"), which works fine with return redirect, but not with return render_template. I tried passing in a flag with return redirect, but my computer didn't like that. You always know the UserId, but navigation is not being tracked (i.e. no cookies). Simple answers that I can understand are preferred to clever ones that I cannot.
# from application.py, at the end of the 'buy' function
# flag says local to 'buy' so this does not help me
flag = True
return redirect("/")
## also tried, but computer got angry
return render_template("/")

I don't really know how the alert looks like but it sounds like you might be looking for flash. The documentation is available here
Also using the example provided in the documentation, the handler for '/buy' should look like buy() below:
from flask import Flask, flash, redirect, render_template, \
request, url_for
app = Flask(__name__)
app.secret_key = 'some_secret'
#app.route('/')
def index():
return render_template('index.html')
#app.route('/buy', methods=['GET', 'POST'])
def buy():
if False:
pass
else:
flash('You were redirected from /buy')
return redirect(url_for('index'))
return render_template('buy.html')

Related

With Flask-Restplus how to create a POST api without making the URL same with GET

I'm new to Flask and Flask-RestPlus. I'm creating a web api where I want keep my POST urls different from the GET urls which are visible in Swagger. For example in Flask-Restplus
#api.route('/my_api/<int:id>')
class SavingsModeAction(Resource):
#api.expect(MyApiModel)
def post(self):
pass #my code goes here
def get(self, id):
pass #my code goes here
So in swagger for the both apis url would look like
GET: /my_api/{id}
POST: /my_api/{id}
But so far I have absolutely no use of {id} part in my post api and it perhaps creates a bit of confusion for an user whether to update an existing record or to create a new, however the purpose of the api is just to create.
It will be better to use query params
like GET: /my_api?id=
your above code will be like
from flask import request
#api.route('/my_api')
class SavingsModeAction(Resource):
#api.expect(MyApiModel)
def post(self):
...
def get(self):
_id = request.args.get("_id", type=int)
...

Request using trio asks returns a different response than with requests and aiohttp

Right, hello, so I'm trying to implement opticard (loyality card services) with my webapp using trio and asks (https://asks.readthedocs.io/).
So I'd like to send a request to their inquiry api:
Here goes using requests:
import requests
r = requests.post("https://merchant.opticard.com/public/do_inquiry.asp", data={'company_id':'Dt', 'FirstCardNum1':'foo', 'g-recaptcha-response':'foo','submit1':'Submit'})
This will return "Invalid ReCaptcha" and this is normal, and what I want
Same thing using aiohttp:
import asyncio
import aiohttp
async def fetch(session, url):
async with session.post(url, data={'company_id':'Dt', 'FirstCardNum1':'foo', 'g-recaptcha-response':'foo','submit1':'Submit'} ) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'https://merchant.opticard.com/public/do_inquiry.asp')
print(html)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Now this also returns "Invalid ReCaptcha", so that's all nice and good.
However now, using trio/asks:
import asks
import trio
async def example():
r = await asks.post('https://merchant.opticard.com/public/do_inquiry.asp', data={'company_id':'Dt', 'FirstCardNum1':'foo', 'g-recaptcha-response':'foo','submit1':'Submit'})
print(r.text)
trio.run(example)
This returns a completely different response with 'Your session has expired to protect your account. Please login again.', this error/message can be accessed normally when inputting an invalid url such as 'https://merchant.opticard.com/do_inquiry.asp' instead of 'https://merchant.opticard.com/public/do_inquiry.asp'.
I have no idea where this error is coming from, I tried setting headers, cookies, encoding, nothing seems to make it work. I tried replicating the issue, but the only way I managed to replicate the result with aiohttp and requests is by setting an incorrect url like 'https://merchant.opticard.com/do_inquiry.asp' instead of 'https://merchant.opticard.com/public/do_inquiry.asp'.
This must be an issue from asks, maybe due to encoding or formatting, but I've been using asks for over a year and never had an issue where a simple post request with data would return differently on asks compared to everywhere else. And I'm baffled as I can't understand why this is happening, it couldn't possibly be a formatting error on asks' part because if so how come this is the first time something like this has ever happened after using it for over a year?
This is a bug how asks handles redirection when a non-standard location is received.
The server returns a 302 redirection with Location: inquiry.asp?... while asks expects it to be a full URL. You may want to file a bug report to asks.
How did I find this? A good way to go is to use a proxy (e.g. mitmproxy) to inspect the traffic. However asks doesn't support proxies. So I turned to wireshark instead and use a program to extract TLS keys so wireshark can decrypt the traffic.

Flask_Sqlalchemy with multithreaded Apache. Sessions out of sync with database

Background: Apache server using mod_wsgi to serve a Flask app using Flask_Sqlalchemy connecting to MySQL. This is a full stack application so it is nearly impossible to create a minimal example but I have tried.
My problem is that when I make some change that should modify the database subsequent requests don't always seem to reflect that change. For example if I create an object, then try to edit that same object, the edit will sometimes fail.
Most of the time if I create an object then go to the page listing all the objects, it will not show up on the list. Sometimes it will show up until I refresh, when it will disappear, and with another refresh it shows up again.
The same happens with edits. Example code:
bp = Blueprint('api_region', __name__, url_prefix='/app/region')
#bp.route('/rename/<int:region_id>/<string:name>', methods=['POST'])
def change_name(region_id, name):
region = Region.query.get(region_id)
try:
region.name = name
except AttributeError:
abort(404)
db.session.add(region)
db.session.commit()
return "Success"
#bp.route('/name/<int:region_id>/', methods=['GET'])
def get_name(region_id):
region = Region.query.get(region_id)
try:
name = region.name
except AttributeError:
abort(404)
return name
After object is created send a POST
curl -X POST https://example.com/app/region/rename/5/Europe
Then several GETs
curl -X GET https://example.com/app/region/name/5/
Sometimes, the GET will return the correct info, but every now and then it will return whatever it was before. Further example output https://pastebin.com/s8mqRHSR it happens at varying frequency but about one in 25 will fail, and it isn't always the "last" value either, when testing it seems to get 'stuck' at a certain value no matter how many times I change it up.
I am using the "dynamically bound" example of Flask_Sqlalchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
... snip ...
return app
Which creates a scoped_session accessible in db.session.
Apache config is long and complicated but includes the line
WSGIDaemonProcess pixel processes=5 threads=5 display-name='%{GROUP}'
I can post more information if required.
For reference if anyone finds this thread with the same issue, I fixed my problem.
My Flask App factory function had the line app.app_context().push() leftover from the early days when it was based off a Flask tutorial. Unfortunately snipped out of the example code otherwise it might have been spotted by someone. During a restructuring of the project this line was left out and the problem fixed itself. Not sure why or how this line would cause this issue, and only for some but not all requests.

Is there a better design for 'as needed' web interface with flask

I have a python program which is doing millions of comparisons across records. Occasionally a comparison fails and I need to have a user (me) step and update a record. Today I do this by calling a function which:
creates a flask 'app'
creates and populates a wtform form to collect the necessary information
instantiates the flask app (e.g. app.run() and webbrowser.open() call to pull up the form)
I update the data in the form, when the form is submitted, the handler puts the updated data into a variable and then shuts down the flask app returning the
data to the caller
This seems kludgy. Is there a cleaner way of doing this recognizing that this is not a typical client-driven web application?
The minimal problem is how best to programmatically launch an 'as needed' web-based UI which presents a form, and then return back the submitted data to the caller.
My method works, but seems a poor design to meet the goal.
As I have not yet found a better way - in case someone else has a similar need, here is how I'm meeting the goal:
...
if somedata_needs_review:
review_status = user_review(somedata)
update_data(review_status)
...
def user_review(data_to_review):
""" Present web form to user and receive their input """
returnstruct = {}
app = Flask(__name__)
#app.route('/', methods=['GET'])
def show_review_form():
form = create_review_form(data_to_review)
return render_template('reviewtemplate.tpl', form=form)
# TODO - I currently split the handling into a different route because
# when using the same route previously Safari would warn of resubmitting data.
# This has the slightly unfortunate effect of creating multiple tabs.
#app.route('/process_compare', methods=['POST'])
def process_review_form():
# this form object will be populated with the submitted information
form = create_review_form(request.form, matchrecord=matchrecord)
# handle submitted updates however necessary
returnstruct['matched'] = form.process_changes.data
shutdown_flask_server()
return "Changes submitted, you can close this tab"
webbrowser.open('http://localhost:5000/', autoraise=True)
app.run(debug=True, use_reloader=False)
# The following will execute after the app is shutdown.
print('Finished manual review, returning {}'.format(returnstruct))
return(returnstruct)
def shutdown_flask_server():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()

Flask - url_for automatically escapes '=' to '%3D'

So.. I'm having some issues with Flask's url_for . The code still works.. but when users navigate to a link that was generated by url_for the link looks bad in the address bar.
Namely, I have a decorated view function as follows:
#app.route("/")
#app.route("/page=<int:number")
def index(number=0):
return "Index Page: {}".format(number)
This all works fine except when I try to generate a url for that route. Calling:
url_for("index", number=10)
Yields: domain.tld:80/page%3D10
Is there any way to circumvent this issue? I'd like for '=' to be returned instead of '%3D' when it's built into the route itself.
I only noticed it was doing this when I was testing it in an assert and discovered that the routes were ending up different from what I expected them to be.
At the moment, I have my test case circumvent the issue by using urllib.parse.unquote to fix the url for testing purposes. I could probably just do that for all urls since I won't have any user input to worry about those causing problems.. but it's there for a reason so.... :P
One option you have is to not build the parameter in to the route itself, but use query parameters instead:
from flask import Flask, render_template, request, url_for
app = Flask(__name__)
#app.route("/")
def index():
page = request.args.get('page', 0, type=int)
print(url_for("index", page=10)) # yields /?page=10
return "Index Page: {}".format(page)
app.run(debug=True)
My making use of query parameters for the route, you avoid the issue of Flask encoding the = sign in the route definition.

Resources