Efficient way of handling XHR request in Python Flask - python

Currently I am using following way of views to determine if request is AJAX / XHR. Accordingly I am returning json data and rendering the template file in Flask. This working perfectly so for in my application.
from flask import request, render_template, jsonify
#cinemascope.route('/cinema')
def cinema():
if request.is_xhr:
cinema = Cinema.query.all()
return jsonify({'cinema_scope': cinema})
return render_template('cinema.html')
But I see this way little problematic as my application might many more #app.routes and every time I will have to repeat the code (if clause) so its loosing upon the maintainability aspect.
I am thinking about the two options here -
Option # 1 - Writing a simple function to wrap the if clause from above route.
Option # 2 - writing a decorator which will determine the request and accordingly it can be called from route - in a way decorator will handle the redundant part of code. I dont know too much about writing custom decorators so far though after reading little about it seems to be the best option.
Any further direction in adopting any of above approaches will be appreciated.
Basically I want to know the way - so I can avoid if clause part of code in each and every #app.route.

I refered - from http://justindonato.com/notebook/template-or-json-decorator-for-flask.html
This is what my decorator looks like
from functools import wraps
def xhr_request_check(template=None):
def decorator_request_parser(f):
#wraps(f)
def decorator_request(*args, **kwargs):
func_data = f(*args, **kwargs)
if request.is_xhr or not template:
return jsonify(func_data)
else:
return render_template(template, **func_data)
return decorator_request
return decorator_request_parser
In my view I have written
from flask import request, render_template, jsonify
#catalog.route('/cinema')
#xhr_request_check('cinema.html')
def cinema():
cinema = Cinema.query.all()
return {'cinema_scope': cinema}
Now I just needs to add #xhr_request_check('cinema.html') to each route and it is working perfectly.
Please let me know if any one would even better solution from code optimization perspective.

The way of doing this would be to create a decorator, which are essentially functions which take a function as an input and return a function as an output. They are not specific to flask, but python in general.
Here is an example for flask:
How to make a python decorator function in Flask with arguments (for authorization)

is_xhr is deprecated, but you could check on the Accepts header instead.

Related

Trying to pass an argument to a function, can get through the ''#app.route'' in python flask

I am trying to pass an argument to a function I made into app.route.
In the following code, I create 2 pages, 1 Homepage and 1 Admin page.
I want to permit or deny access to the /admin page depending if I have the parameter ADMINpass set to False or True in my python code directly.
I understand that in this particular situation, it will work, as I don't need to pass the argument, but if I define my admin page in another subscript and import it in mainscript, it will tell me that ''admin takes exactly 1 argument (0 given). So I want this function to work for whatever case.
I don't really worry about security for now as I am testing things.
Thanks alot and apologies if a similar question has already been posted. Here is the code:
###User section, importing parameters
ADMINpass=False
###End of user section
##Main code
#app.route('/') #Home page
def homepage():
return 'Hello, main page here'
#app.route('/admin') #Admin page that I want to access
def admin(ADMINpass): #Trying to pass the argument here will not work, probably because of the app.route
if ADMINpass==False: #If admin not activated in code, redirect to homepage
a=redirect(url_for('homepage'))
if ADMINpass == True: #If admin is activated, permit access
print('This is the admin page')
if __name__== '__main__':
app.run()
There are several issues here.
The #app.route decorator is a wrapper around the admin() function. It provides the extra functionality that does route registration (the thing that allows the '/admin' string to be an endpoint), request handling (you could specify HTTP methods like 'POST' with it), and more (see Flask docs for route()).
So if you did this:
#app.route('/admin') #Admin page that I want to access
def admin(ADMINpass): #Trying to pass the argument here will not work, probably because of the app.route
Flask expects you to use this for parameterising the endpoint itself:
#app.route('/endpoint/<something>')
def func(something):
return something
$ curl localhost:5000/endpoint/value_of_something
value_of_something
I would suggest Miguel Grinberg's guide to decorators for more info on decorators with a focus on Flask.
Coming back to your issue, you could do this (but read on to see why I think you shouldn't):
ADMINpass=False
#app.route('/endpoint')
def admin():
global ADMINpass
return f'ADMINpass is {ADMINpass}'
The problem with global variables is that they could be changed at any point in some other piece of code. This is generally a good way to introduce mistakes in your code and make your code less maintainable and more difficult to test.
With servers, the problem is more fundamental - what if your code grew to look something like this?
ADMINpass=False
#app.route('/endpoint')
def admin():
global ADMINpass
ADMINpass = not ADMINpass
return f'ADMINpass is {ADMINpass}'
Every request (which may be coming from different clients) would change it, so its value is nearly impossible to reason about:
$ curl localhost:5000/endpoint
ADMINpass is False
$ curl localhost:5000/endpoint
ADMINpass is True
$ curl localhost:5000/endpoint
ADMINpass is False
You might be looking for Flask session objects. The values stored in the session object are specific to each client because its implementation is based on cookies. If it's an authentication mechanism implementation you're going towards, this may be exactly what you need.
from flask import session
app.something = 'value_of_something_else'
#app.route('/endpoint')
def admin():
return app.something
$ curl localhost:5000/endpoint
value_of_something_else
If a single static value is what you need, one way to overcome the errors you're likely getting when trying to use the variable would be to use a function you can always call:
def admin_pass():
return False
#app.route('/endpoint')
def admin():
if not admin_pass():
...

How to change routes in Flask?

I have watched a tutorial on Flask and I can't seem to understand how does this code work
def my_home():
return render_template("./index.html")
#app.route('/<string:page_name>')
def html_page(page_name):
return render_template(page_name)```
Specifically /<string:page_name> is confusing to me, how does it understand what is page_name and redirect to that page?
Thank you!
The answer to this is in the magic of decorators and not flask itself. A decorator is a high level function which accepts a function and manipulates it accordingly. See the following example:
def route(func, user_path):
# internal flask mechanisms here
def callback(user_path):
return http.route(user_path, func)
return callback
#route("hello_world")
def render():
return "Hello World"
The decorator is taking your function as an input and performing some actions to correlate the path with your given input. This can obviously be used for many other purposes. For example, flask also allows an additional decorator to define the type of request allowed for the function, such as GET, POST, etc.
To answer your question, flask is accepting a URL parameter and generating the page based on that given parameter. This is known as dynamic routing and is used for database query purposes based on the route (though the given example is trivial). So if someone goes to page_name "John", you can use that value and template it in your html to dynamically say hi to John (and whomever else).
/<string:page_name>
means that Flask expects a customised string after your domain i.e.
www.yourdomain.com/anycustomisedstring
/<data_type:your_variable_name> i.e. /<integer:page_number>
in this case, your variable is passed as a parameter to your function. This function returns the site that is passed in the URL as a string

Flask trying to call function with a redirect

I’m using Flask with pluggable views. Having some trouble getting a redirect to work. Here is the code.
def some_redirect():
*condition for redirect*
return redirect(url)
class SomeClass(View):
*few methods*
def dispatch_request(self):
some_redirect()
*other stuff*
Essentially all I want to do is check is either a certain value in session exists or if a certain config key is present. If not, I’d like to redirect to an external url. Using some print statements I can see that the code reaches the redirect. But instead of sending me to someurl.com, it continues in dispatch request. I don’t want to use a decorator here because I’m not really modifying any of these methods. I essentially just want to redirect to the given url if those conditions are not met. Any help would be much appreciated.
You need to return the result of the function:
def dispatch_request(self):
redirect = redirect_if_unauthorized()
if redirect:
return redirect

Flask: How do I get the returned value from a function that uses #app.route decorator?

So I'm pretty new to Flask and I'm trying to make my mind around one thing. So, if I understand well when you write a function within a Flask app and you use the #app.route decorator in that function, it only runs when you hit that path/url.
I have a small oauth app written in Flask that goes through all the authorization flow and then it return the token.
My question is how do I get that token from the #decorated function? For example, lets say I have something like this:
#app.route(/token/)
def getToken(code): #code from the callback url.
#/Stuff to get the Token/
#/**********************/
return token
If I hit the (/token/) url-path the function returns the token. But now I need to get that token and use it in another function to write and read from the API I just got the token from. My initial thought was doing this:
token = getToken(code)
But if I do that, I get this error:
RuntimeError: working outside of request context
So again, my question is, how do I get the token so I can pass it as a parameter to other functions.
Extract the token generation code into a separate function, so that you can call it from anywhere, including the view function. It's a good practice to keep the application logic away from the view, and it also helps with unit testing.
I assume your route includes a placeholder for code, which you skipped:
def generateToken(code):
#/Stuff to get the Token/
#/**********************/
return token
#app.route('/token/<string:code>')
def getToken(code):
return generateToken(code)
Just keep in mind that generateToken shouldn't depend on the request object. If you need any request data (e.g. HTTP header), you should pass it explicitly in arguments. Otherwise you will get the "working outside of request context" exception you mentioned.
It is possible to call request-dependent views directly, but you need to mock the request object, which is a bit tricky. Read the request context documentation to learn more.
not sure what the context is. You could just call the method.
from yourmodule import get_token
def yourmethod():
token = get_token()
Otherwise, you could use the requests library in order to retrieve the data from the route
>>> import requests
>>> response = requests.get('www.yoursite.com/yourroute/')
>>> print response.text
If you're looking for unittests, Flask comes with a mock client
def test_get_token():
resp = self.app.get('/yourroute')
# do something with resp.data

How can I get a rewritten render_to_response to get a json in django with the least changes to the whole project

I now receive a task, that is developing a native app on phone based on a web project. So I think the best way to communicate between server and client is using json.
Then I need all the response give me a result of json format. But now in the web project, almost every view use render_to_response like the following:
ctxt = RequestContext(request,*args)
return render_to_response('XXX.html',ctxt)
Then I want rewrite render_to_response like below, so I need not to change every one to a new function:
def render_to_response:
return new HttpResponse(simplejson.dumps(*args), mimetype='text/json'))
Obviously, I can change the source code in django, but it's not a practicable method. Because other project also need use it.
Or I can write a new module with the render_to_response function, and import this after
from django.shortcuts import render_to_response
But I am not sure if this is possible.
If this can work, I also need to add this import into every .py files which is still a huge work.
So I want a best way with the least changes to the whole project.
Try replacing the return you have with this
return HttpResponse(json.dumps(ctxt), content_type="application/json")
On a different note, you do not really need to return the context if the client requesting it doesn't need it. You might be better of return a simple message like "Success"
Another solution is to include django-annoying in your project and use the #ajax_request decorator on all your views which want to respond as json.
#ajax_request
def my_view(request):
news = News.objects.all()
news_titles = [entry.title for entry in news]
return {'news_titles': news_titles}
Refer example here - https://bitbucket.org/offline/django-annoying/wiki/Home

Categories