I have this Flask View which takes a POST and GET requests
Goal is to do something with the Data from the POST request
and use it for the GET request
for example this AJAX GET Request
$.getJSON({url: '/uploadajax'}).done(result =>console.log(result));
which waits to return the processed data from the POST request
I was able to pass the data to the AJAX call by
declaring the global variable result and changed it in the function
and use it as a return value for the GET Request
Question here: is there a cleaner way to perform this task ?
result = 0
# ------------upload-file-----------------------------------------#
#flask_class.route('/uploadajax', methods=['POST', 'GET'])
def receave_file():
if request.method == 'POST':
uploaded_file = request.files['file']
# filename = secure_filename(uploaded_file.filename)
if uploaded_file.filename != "":
filename = secure_filename(uploaded_file.filename)
file_ext = os.path.splitext(filename)[1] # was macht das ?
if file_ext not in Config.ALLOWED_EXTENSIONS:
abort(400)
# file kann auch net gespeichert werden
uploaded_file.save(os.path.join(flask_class.instance_path, 'uploads', filename))
# ------------------------------------- #
df = pd.read_excel(uploaded_file)
columns = df.columns.to_list()
global result
result = json.dumps(columns)
# return result
print("shoud return somehting")
# ---------------------------------------- #
return '', 204
# ---------------------------------------- #
else:
return "false"
else:
# GET REQUEST
if len(result) > 1:
return result
else:
return '', 404
# return render_template('index.html')
Yes, there is :)
Have a look at the following code:
class LocalStore:
def __call__(self, f: callable):
f.__globals__[self.__class__.__name__] = self
return f
# ------------upload-file-----------------------------------------#
#flask_class.route('/uploadajax', methods=['POST', 'GET'])
#LocalStore() # creates store for this unique method only
def receave_file():
if request.method == 'POST':
LocalStore.post_headers= request.headers
LocalStore.post_body = request.body
LocalStore.post_json = request.get_json()
LocalStore.post_params = request.params
LocalStore.answer_to_everything = 42
print("POST request stored.")
return jsonify({"response": "Thanks for your POST!"})
else:
try:
print("This is a GET request.")
print("POST headers were:", LocalStore.post_headers)
print("POST params were :", LocalStore.post_params)
print("POST body was :", LocalStore.post_body)
print("The answer is :", LocalStore.answer_to_everything)
return jsonify({"postHeadersWere": LocalStore.post_headers})
except AttributeError:
return jsonify({"response":"You have to make a POST first!"})
I created a special class which "injects" its reference into the __globals__ dictionary of the method. If you type the class name in the method, it will be the object reference, not the class reference. Be aware of that!
You then just need to add #LocalStore underneath the #app.route(...) of your application because the store needs to be routed with the method...
I think it's a quite elegant way that saves you the definition of 5 global variables for 5 different methods
Related
I have a function with a method put, but a want other function with method patch, but I don't know how do it.
Here a method put:
#app.route('/api/v1/benefit/', methods=['PUT'])
def update_benefited(id):
benefited = Benefited.query.filter_by(id=id).first()
if benefited is None:
return jsonify({'message': 'Benefited does not exists'}), 404
json = request.get_json(force=True)
if json.get('username') is None:
return jsonify({'message': 'Bad request'}), 400
benefited.username = json['username']
benefited.age = json['age']
benefited.job = json['job']
benefited.update()
return jsonify({'benefited': benefited.json()})
I want to check if an object exists - if it doesn't exist I want to still continue the function and not return a 404 error. How can I achieve this?
def check(request):
if request.is_ajax():
# print('Working') #prints
id = request.POST.get('id')
post = Post.objects.get(hash=id)
obj = get_object_or_404(Post, post=post)
if obj:
# do stuff
else:
#do something else
The above code returns:
Not Found: /check/
[22/Jul/2018 01:15:03] "POST /check/ HTTP/1.1" 404 1729
Simply catch the Post.DoesNotExist exception and do something else in the exception handler:
def check(request):
if request.is_ajax():
id = request.POST.get('id')
try:
post = Post.objects.get(hash=id)
# do stuff
except Post.DoesNotExist:
# do something else
If you use this often then you could make it as function and save it in helpers.py
def get_or_none(model, *args, **kwargs):
try:
return model.objects.get(*args, **kwargs)
except model.DoesNotExist:
return None
and use it as
post = get_or_none(Post, hash=id)
if post:
#do something
else:
#do other thing
I'm working on a PUT request to be able to modify data in my JSON file, using Flask and Python. The problem is it won't save the changes made.
Below is my code:
#app.route('/updated', methods = ['POST', 'PUT' 'GET'])
def update():
try:
title = request.form['title']
print title
if request.method == 'POST':
with open("articles.json", 'r+') as json_File:
articles = json.load(json_File)
for article in articles['article']:
if title == article['title']:
print article['title']
print article['author']
print article['article_id']
article['title'] = title
article['author'] = request.form['author']
article['text'] = request.form['text']
article['article_id'] = request.form['article_id']
print article
save_article = json.dumps(article, json_File)
else:
print "article could not be added"
#json_File.close()
return render_template('updated.html', save_article = save_article, article = article)
except:
print "This didn't work."
return render_template('errorHandler.html'), 404
Example from (http://blog.luisrei.com/articles/flaskrest.html)
#app.route('/echo', methods = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'])
def api_echo():
if request.method == 'GET':
return "ECHO: GET\n"
elif request.method == 'POST':
return "ECHO: POST\n"
elif request.method == 'PATCH':
return "ECHO: PACTH\n"
elif request.method == 'PUT':
return "ECHO: PUT\n"
elif request.method == 'DELETE':
return "ECHO: DELETE"
Probably best to have a if/elif/else for each method in the decorator, prevents weird bug and edge cases.
I think you should change this part:
if request.method == 'POST' or request.method == 'PUT':
For better practices, i think you should do:
if request.method == 'POST' or request.method == 'PUT':
# do your code here, which edit into your database
if request.method == 'GET':
# do GET code here, which return data from your database
Or separate your https methods into different functions
First of all, json.dumps() "dumps" to a string, not a file. So
save_article = json.dumps(article, json_File)
will return a string which is then bound to the save_article variable, but the file is not actually modified. You probably meant to use json.dump(article, json_File) which does accept a file as the second argument.
Note: The file argument is silently ignored in Python 2, which I assume that you are using because it would show up as an error in Python 3.
There might be other problems. One is that articles will be appended to the file, but it would seem that the intention of the code is to update an existing article. It's generally impractical to update text files in place. A better method would be to iterate over the articles, updating those that match the title. Then rewrite the whole file once at the end. Here's an example:
with open("articles.json", 'r') as json_File:
articles = json.load(json_File)
# update any matching articles
for article in articles['article']:
if title == article['title']:
article['author'] = request.form['author']
article['text'] = request.form['text']
article['article_id'] = request.form['article_id']
# rewrite the whole JSON file with updated dictionary
with open("articles.json", 'w') as json_File:
json.dump(articles, json_File)
As you are updating the article data you might want to consider using a simple database to manage it. You could take a look at Flask SQLAlchemy.
I am writing a generic wrapper around a REST API. I have several functions like the one below, responsible for retrieving a user from its email address. The part of interest is how the response is processed, based on a list of expected status codes (besides HTTP 200) and callbacks associated to each expected status code:
import requests
def get_user_from_email(email):
response = requests.get('http://example.com/api/v1/users/email:%s' % email)
# define callbacks
def return_as_json(response):
print('Found user with email [%s].' % email)
return response.json()
def user_with_email_does_not_exist(response):
print('Could not find any user with email [%s]. Returning `None`.' % email),
return None
expected_status_codes_and_callbacks = {
requests.codes.ok: return_as_json, # HTTP 200 == success
404: user_with_email_does_not_exist,
}
if response.status_code in expected_status_codes_and_callbacks:
callback = expected_status_codes_and_callbacks[response.status_code]
return callback(response)
else:
response.raise_for_status()
john_doe = get_user_from_email('john.doe#company.com')
print(john_doe is not None) # True
unregistered_user = get_user_from_email('unregistered.user#company.com')
print(unregistered_user is None) # True
The code above works well so I want to refactor and generalize the response processing part. I would love to end up with the following code:
#process_response({requests.codes.ok: return_as_json, 404: user_with_email_does_not_exist})
def get_user_from_email(email):
# define callbacks
def return_as_json(response):
print('Found user with email [%s].' % email)
return response.json()
def user_with_email_does_not_exist(response):
print('Could not find any user with email [%s]. Returning `None`.' % email),
return None
return requests.get('https://example.com/api/v1/users/email:%s' % email)
with the process_response decorator defined as:
import functools
def process_response(extra_response_codes_and_callbacks=None):
def actual_decorator(f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
response = f(*args, **kwargs)
if response.status_code in expected_status_codes_and_callbacks:
action_to_perform = expected_status_codes_and_callbacks[response.status_code]
return action_to_perform(response)
else:
response.raise_for_status() # raise exception on unexpected status code
return wrapper
return actual_decorator
My problem is the decorator complains about not having access to return_as_json and user_with_email_does_not_exist because these callbacks are defined inside the wrapped function.
If I decide to move the callbacks outside of the wrapped function, for example at the same level as the decorator itself, then the callbacks have no access to the response and email variables inside the wrapped function.
# does not work either, as response and email are not visible from the callbacks
def return_as_json(response):
print('Found user with email [%s].' % email)
return response.json()
def user_with_email_does_not_exist(response):
print('Could not find any user with email [%s]. Returning `None`.' % email),
return None
#process_response({requests.codes.ok: return_as_json, 404: user_with_email_does_not_exist})
def get_user_from_email(email):
return requests.get('https://example.com/api/v1/users/email:%s' % email)
What is the right approach here? I find the decorator syntax very clean but I cannot figure out how to pass the required parts to it (either the callbacks themselves or their input arguments like response and email).
You could convert the decorator keys into strings, and then pull the inner functions from the outer function passed to the decorator via f.func_code.co_consts. Don't do it this way.
import functools, new
from types import CodeType
def decorator(callback_dict=None):
def actual_decorator(f):
code_dict = {c.co_name: c for c in f.func_code.co_consts if type(c) is CodeType}
#functools.wraps(f)
def wrapper(*args, **kwargs):
main_return = f(*args, **kwargs)
if main_return['callback'] in callback_dict:
callback_string = callback_dict[main_return['callback']]
callback = new.function(code_dict[callback_string], {})
return callback(main_return)
return wrapper
return actual_decorator
#decorator({'key_a': 'function_a'})
def main_function(callback):
def function_a(callback_object):
for k, v in callback_object.items():
if k != 'callback':
print '{}: {}'.format(k, v)
return {'callback': callback, 'key_1': 'value_1', 'key_2': 'value_2'}
main_function('key_a')
# key_1: value_1
# key_2: value_2
Can you use classes? The solution is immediate if you can use a class.
As mentioned in the comments for my other answer, here is an answer that uses classes and decorators. It's a bit counter-intuitive because get_user_from_email is declared as a class, but ends up as a function after decorating. It does have the desired syntax however, so that's a plus. Maybe this could be a starting point for a cleaner solution.
# dummy response object
from collections import namedtuple
Response = namedtuple('Response', 'data status_code error')
def callback_mapper(callback_map):
def actual_function(cls):
def wrapper(*args, **kwargs):
request = getattr(cls, 'request')
response = request(*args, **kwargs)
callback_name = callback_map.get(response.status_code)
if callback_name is not None:
callback_function = getattr(cls, callback_name)
return callback_function(response)
else:
return response.error
return wrapper
return actual_function
#callback_mapper({'200': 'json', '404': 'does_not_exist'})
class get_user_from_email:
#staticmethod
def json(response):
return 'json response: {}'.format(response.data)
#staticmethod
def does_not_exist(response):
return 'does not exist'
#staticmethod
def request(email):
response = Response('response data', '200', 'exception')
return response
print get_user_from_email('blah')
# json response: response data
Here's an approach that uses function member data on class methods in order to map the response function to the appropriate callback. This seems like the cleanest syntax to me, but still has a class turning into a function (which could be easily avoided if desired).
# dummy response object
from collections import namedtuple
Response = namedtuple('Response', 'data status_code error')
def callback(status_code):
def method(f):
f.status_code = status_code
return staticmethod(f)
return method
def request(f):
f.request = True
return staticmethod(f)
def callback_redirect(cls):
__callback_map = {}
for attribute_name in dir(cls):
attribute = getattr(cls, attribute_name)
status_code = getattr(attribute, 'status_code', '')
if status_code:
__callback_map[status_code] = attribute
if getattr(attribute, 'request', False):
__request = attribute
def call_wrapper(*args, **kwargs):
response = __request(*args, **kwargs)
callback = __callback_map.get(response.status_code)
if callback is not None:
return callback(response)
else:
return response.error
return call_wrapper
#callback_redirect
class get_user_from_email:
#callback('200')
def json(response):
return 'json response: {}'.format(response.data)
#callback('404')
def does_not_exist(response):
return 'does not exist'
#request
def request(email):
response = Response(email, '200', 'exception')
return response
print get_user_from_email('generic#email.com')
# json response: generic#email.com
You could pass the function parameters of the outer function to the handlers:
def return_as_json(response, email=None): # email param
print('Found user with email [%s].' % email)
return response.json()
#process_response({requests.codes.ok: return_as_json, 404: ...})
def get_user_from_email(email):
return requests.get('...: %s' % email)
# in decorator
# email param will be passed to return_as_json
return action_to_perform(response, *args, **kwargs)
I think I may be overlooking something simple. I have a class with a constructor defined. In one of the functions I make a call to an api an store the response in a variable. I'd like to return that variable. kind of like, info = MyClass(), data = info.somefunctuon(). In my case data always returns None.
here is a snippet of what I have.
class SomeAPI(object):
def __init__(self, br, cj, username, password, logged_in='', api_id='', data=''):
self.br = br
self.cj = cj
self.logged_in = False
self.username = username
self.password = password
self.api_id = api_id
br.set_cookiejar(self.cj)
br.set_handle_robots(False)
def _login_check(f):
#wraps(f)
def wrapper(self):
if self.log_in():
f(self)
else:
raise MyError('Not Logged In')
return wrapper
def log_in(self):
auth_data = {'cmd': 'auth', 'params': {'username': self.username,
'password': self.password}}
if not self.logged_in:
self.br.open('https://someurl.com/auth', dumps(auth_data))
data = loads(self.br.response().read())
status = data['response']['status']
if status == 'OK':
api_id = data['response']['api_id']
self.logged_in = True
self.api_id = api_id
else:
raise MyError(status)
return self.logged_in
#_login_check
def campaigns_list(self):
campaigns_list_data = {'cmd': 'ab_campaigns'}
raw = self.br.open('https://someurl.com/{}'.format(self.api_id),
dumps(campaigns_list_data))
json_resp = raw.read()
resp = loads(json_resp)
status = resp['response']['status']
if status == 'OK':
data = resp['response']['Ad Campaigns']
return data
when I do:
info = SomeAPI(mechanize.Browser(), cookielib.LWPCookieJar(), 'MyName', '12345')
data = info.campaigns_list()
print data
I always get None. I know that I get the data because when i replace return with print I get the expected response. I thought maybe I'd need to define data on the constructor, but then each function of my class would need its own variable defined. I think it would get messy.
Any suggestions please let me know. Thank you!
Your decorator _login_check calls the function it wraps, but it doesn't return whatever the function returns.
Change:
def _login_check(f):
#wraps(f)
def wrapper(self):
if self.log_in():
f(self)
else:
raise MyError('Not Logged In')
return wrapper
to:
def _login_check(f):
#wraps(f)
def wrapper(self):
if self.log_in():
return f(self)
else:
raise MyError('Not Logged In')
return wrapper