Add Flask routes to a class's methods [duplicate] - python

This question already has answers here:
Extending Flask class as main App
(1 answer)
Python3 Flask - missing 1 required positional argument: 'self'
(1 answer)
Closed 5 years ago.
I'm trying to write a server to manage some data and serve webpages. I'd like to use Flask, the Python web framework.
I can easily set up a "static" Flask app that serves a page for fixed data, doing something like
from flask import Flask, render_template
DATA = [1, 2, 3]
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
#app.route('/')
def index():
return render_template('mytemplate.html', data=DATA)
if __name__ == '__main__':
app.run()
but what I'd like is a "dynamic" app, with a Server which manages changing data. I'd like the Server to serve the most current data when a page is opened. So I'd like to do something like the following:
from flask import Flask, render_template
app = Flask(__name__)
class Server():
def __init__(self, data):
self.data = data
###
### Other long-running methods which might update self.data
###
#app.route('/') # <--- causes an error
def index(self):
return render_template('mytemplate.html', data=self.data)
if __name__ == '__main__':
s = Server([1, 2, 3])
### Spawn long-running methods
app.run()
However, doing this raises an error:
TypeError: index() takes exactly 1 argument (0 given)
I've seen that I can achieve what I want by wrapping my methods in Flask routes outside the class definition, after instantiating my server, by doing this:
if __name__ == '__main__':
s = Server([1, 2, 3])
### Spawn long-running methods that might update s.data
app.route('/')(s.index)
app.run()
but this seems exceptionally messy! It would be so much clearer and more logical if the routes were attached to the actual methods I'd like to use to serve the pages.
So my question is: is there a way to add Flask routes directly to methods inside a class?
Many thanks in advance

Related

Python Flask Redirect URL with all GET parameters dynamically?

how can I dynamically pass all of the GET parameters from one url to another via flask?
This is currently what I am doing:
import os
from flask import Flask,redirect
from flask import request
from flask import url_for
app = Flask(__name__)
#app.route('/')
def hello():
return redirect(url_for("https://myurl.com", **request.args))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
I can't really do it statically. What I am trying to accomplish:
myflaskserver:5000/page?url=google.com&header=body&identity=flash -> https://myurl.com/page?url=google.com&header=body&identity=flash
myflaskserver:5000/dance?url=dance.com&function=dancer&move=quality-> https://myurl.com/dance?url=dance.com&function=dancer&move=quality-
myflaskserver:5000/quit?host=google.com&language=english&password=test1234-> https://myurl.com/quit?host=google.com&language=english&password=test1234
With minimal code, without procedurally having to use if statements, or doing it statically with GET parameters for each page.
Thank you.
Because of the way that the question is phrased, it seems that you are looking for a way to perpetuate the url parameters only. In other words, you're not asking how to perpetuate the url page path (or "routes" in flask terminology), perhaps because you already have a strategy for that in mind.
If that assumption is incorrect, see my note near the bottom of this answer.
It also seems that you prefer passing the parameters as url parameters (versus passing the same data in the request header or payload).
If both these assumptions are correct, then the following approach may work for you:
Use the query_string method of request. This method returns all the url parameters as a bytes object (meaning you probably will need to decode it to a string if you wish to join it to your new url string).
import os
from flask import Flask,redirect
from flask import request
app = Flask(__name__)
#app.route('/')
def hello():
request_parameters = request.query_string.decode('utf-8')
return redirect("https://myurl.com?{0}".format(request_parameters))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Given the first url in your example...
myflaskserver:5000/page?url=google.com&header=body&identity=flash
...the statement request.query_string.decode('utf-8') would return the string "url=google.com&header=body&identity=flash", which is then appended to your endpoint "https://myurl.com?".
Notice ? needs to be added to the endpoint before joining with request.query_string.
Passing page paths (i.e. routes)
This was not explicitly asked in the question, so I won't go into much detail. But if you need to parse the route from the request, you could use request.url_rule, which will return everything after the domain and before the url parameters.
import os
from flask import Flask,redirect
from flask import request
app = Flask(__name__)
#app.route('/hello')
def hello():
request_parameters = request.query_string.decode('utf-8')
route = request.url_rule
return redirect("https://myurl.com{0}?{1}".format(route,request_parameters))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Or you could look into flask.referrer, which will return the referring url from the request header if it is available, which it sometimes isn't, such as in cross-origin scenarios of local testing.
Sidebar
For the information of anyone passing by this post, it may be helpful to mention why the request as written in the question will fail:
When the endpoint "https://myurl.com" of url_for is called, it will fail because the method expects an endpoint (or route, in flask terminology) defined by your app (versus an external endpoint with schema and domain) when called from an active request like this.

How do you make flask execute a function via the app.run command?

I have been trying to follow the tutorials to get flask apps to run on Heroku, like this one: https://dev.to/emcain/how-to-set-up-a-twitter-bot-with-python-and-heroku-1n39.
They all tell you to put this in your code in a file server.py:
from flask import Flask
app = Flask(__name__)
app.run(host='0.0.0.0')
And then run the app via the following command:
python3 server.py
But the tutorials don't explain how to connect the actual function you want to run using the app. In my case, I have a File testbot.py that has the function test(arg1) that contains the code I want to execute:
def test(arg1):
while(1):
#do stuff with arg1 on twitter
I want to do something like this:
from flask import Flask
from testbot import test
from threading import Thread
app = Flask(__name__)
app.addfunction(test(arg1='hardcodedparameter'))
app.run(host='0.0.0.0')
So that when the app runs my test() function executes with the argument. Right now my server is starting, but nothing is happening.
Am I thinking about this correctly?
*Edit: I got it working with the solution, so my server.py now looks like this:
from flask import Flask
from testbot import test
def main_process():
test("hardcodeparam")
app = Flask(__name__)
Thread(target=main_process).start()
app.run(debug=True,host='0.0.0.0')
And now test runs as expected.
Before app.run, register the function with a path, e.g.
#app.route('/')
def test(): # no argument
... do one iteration
return 'ok'
Then visiting the URL will trigger the function. Sites such as https://cron-job.org/ can automate that visiting on a regular basis for free, as suggested here.
If the regular intervals aren't good enough, then you could try:
#app.route('/')
def index(): # no argument
return 'ok'
def test():
while True:
# do stuff
from threading import Thread
Thread(target=test).start()
app.run(...)
You will probably still need to have a job regularly visiting the URL so that Heroku sees that the server is alive and in use.

Running two process on Flask with common variables

I want to build a Webapp with Flask where some data is printed on a dynamic page in real time.
The data is taken from a Python script which connects to a Websocket, then it's printed on the frontend with Flask.
I have two problems:
1) I can't run both the scripts together
2) I don't know how to call parsed from test to yield
Here is the code:
from time import sleep
from flask import Flask, render_template
import websocket
from bitmex_websocket import Instrument
from bitmex_websocket.constants import InstrumentChannels
from bitmex_websocket.constants import Channels
import json
from threading import Thread, Event
app = Flask(__name__)
websocket.enableTrace(True)
channels = [
InstrumentChannels.trade,
]
XBTUSD = Instrument(symbol='XBTUSD',
channels=channels)
XBTUSD.on('action', lambda msg: test(msg))
def test(msg):
parsed = json.loads(json.dumps(msg))
print(parsed)
#app.route('/')
def index():
# render the template (below) that will use JavaScript to read the stream
return render_template('index.html')
#app.route('/stream_sqrt')
def stream():
def generate():
yield '{}\n'.format('test')
return app.response_class(generate(), mimetype='text/plain')
if __name__ == '__main__':
XBTUSD.run_forever()
app.run()
If i put XBTUSD.run_forever() before app.run() i will start the part supposed to retrieve the data but the Flask app won't start. If i do the opposite, the Flask app will run but not the other part. How can i run together the whole app? How could i "share" variables between test and generate?
An easier way to go, please use flask-socketio instead flask.
https://flask-socketio.readthedocs.io/en/latest/
Sample for sending messages using flask-socketio
https://flask-socketio.readthedocs.io/en/latest/#sending-messages

Passing id in route producing uncallables [duplicate]

This question already has an answer here:
Flask view raises TypeError: 'bool' object is not callable
(1 answer)
Closed 6 years ago.
I am trying to transition my workflow into Flask to write a simple web interface for a Python script.
However, doing the following, raises a type error constantly:
from flask import Flask, render_template, request
import sqlite3
app = Flask(__name__)
#app.route('/restart/<int:id>')
def restart(id):
return id
if __name__ == '__main__':
app.run()
I would basically just like to show the id that is passed in the URL.
Am I missing something? This is exactly how I would do this in Django for example and all the examples on the Net have pointed to this approach in Flask.
Your route function should be returning a string but you're returning the integer you're passing into it. Cast it to a string instead:
from flask import Flask, render_template, request
import sqlite3
app = Flask(__name__)
#app.route('/restart/<int:id>')
def restart(id):
return str(id)
if __name__ == '__main__':
app.run()

Flask dynamic route not working - Real Python

I'm working through RealPython and I'm having trouble with the flask dynamic route.
Everything seemed to work until the dynamic route. Now if I try to enter a "search query" (i.e. localhost:5000/test/hi) the page is not found. localhost:5000 still works fine.
# ---- Flask Hello World ---- #
# import the Flask class from the flask module
from flask import Flask
# create the application object
app = Flask(__name__)
# use decorators to link the function to a url
#app.route("/")
#app.route("/hello")
# define the view using a function, which returns a string
def hello_world():
return "Hello, World!"
# start the development server using the run() method
if __name__ == "__main__":
app.run()
# dynamic route
#app.route("/test/<search_query>")
def search(search_query):
return search_query
I can't see that other people using RealPython have had an issue with the same code, so I'm not sure what I'm doing wrong.
The reason why this is not working is because flask never learns that you have another route other / and /hello because your program gets stuck on app.run().
If you wanted to add this, all you need to do would be to add the new route before calling app.run() like so:
# ---- Flask Hello World ---- #
# import the Flask class from the flask module
from flask import Flask
# create the application object
app = Flask(__name__)
# use decorators to link the function to a url
#app.route("/")
#app.route("/hello")
# define the view using a function, which returns a string
def hello_world():
return "Hello, World!"
# dynamic route
#app.route("/test/<search_query>")
def search(search_query):
return search_query
# start the development server using the run() method
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True, port=5000)
Now this will work.
Note: You don't need to change the run configurations inside of app.run. You can just use app.run() without any arguments and your app will run fine on your local machine.
Try using the entire URL instead of just the IP address.

Categories