testing that a json can be rendered as a highchart - python

I want to make a site that makes highcharts. However, on the backend, I want to confirm that the json I am getting back is a valid highchart. I would like to do this with some kind of decorator, eg,
#app.route('/this_is_a_chart')
#chart
def make_chart():
return jsonify({
# some generated chart data
})
then it would catch that this should be a highchart, and test that it is valid. How is this possible in flask?

The decorator would look like this, but you need to tell us how to validate the json and how exactly you want to handle the error. This decorator throws an exception.
def chart( func ):
def r():
json = func()
if not_valid_json( json ):
raise "NotAHighChart"
return json
return r

Related

Can't mock a third party api call

I have a view that perform a third party API call:
from .atoca_api_calls import atoca_api_call
def atoca_api_call_view(request):
# some code
data = atoca_api_call(**params) # the actual API call happens in this func
# some code
My intention is to mock just the atoca_api_call() func and not the whole view.
class AtocaTestCase(TestCase):
def setUp(self):
# some code
#patch('crm.atoca_api_calls.atoca_api_call')
def test_atoca_call(self, mock_atoca_api_call):
mock_atoca_api_call.return_value = MagicMock(
status_code=200,
response=some_json # just returns some json
)
url = reverse('crm:atoca-api-call') # url related to `atoca_api_call_view`
response = self.client.post(url, some_others_params)
# various asserts
The test works fine but atoca_api_call() is not mocked.
I'm aware of where to patch:
#patch('crm.views.atoca_api_call', autospec=True)
Raises a ValueError:
ValueError: Failed to insert expression "<MagicMock name='mock.__getitem__().__getitem__().__getitem__()().resolve_expression()' id='140048216397424'>" on crm.Company.atoca_id. F() expressions can only be used to update, not to insert.
It's probably a simple issue I don't catch for inexperience, any help is really appreciated.
As i see the atoca_api_call() it is mocked. The issue is the return value.
mock_atoca_api_call.return_value = MagicMock(
status_code=200,
response=some_json # just returns some json
)
This should ve a proper response i assume a JsonResponse or Response(data) not sure what are you returning. Try with:
mock_atoca_api_call.return_value = JsonResponse(
some_json # just returns some json
)

Flask dynamic page content

I am trying to write a dynamic page in Python with Flask on my pythonanywhere.com free hosting. I have the following code, hoping I could write to the resp variable to make my pages.
#app.route('/xdcc-search/search.html')
def search_app():
try:
with open('templates/xdcc-search/search.html', 'r') as dynamic:
dynamic.read()
except:
pass
dynamic.replace("<file>","MY_FILENAME.tar")
resp = make_response(render_template(dynamic), 200)
no_cache(resp)
return resp
I get an error stating dynamic is referenced before assignment. Is there a way to edit the template after render_template(filename) retreives and assembles the page?
When you do this:
with open('templates/xdcc-search/search.html', 'r') as dynamic:
dynamic.read()
...you are reading in the contents of the file, but you are throwing them away -- read() is a function that reads the contents of the file and returns them.
Fixing your code so that it actually does what you are trying to do gives this:
#app.route('/xdcc-search/search.html')
def search_app():
try:
with open('templates/xdcc-search/search.html', 'r') as dynamic:
contents = dynamic.read()
except:
pass
contents.replace("<file>","MY_FILENAME.tar")
resp = make_response(render_template(contents), 200)
no_cache(resp)
return resp
...but that is still wrong; render_template takes as its parameter the name of the file that contains the template, not its contents. So what you need to do to get this working would be to replace that render_template with render_template_string.
#app.route('/xdcc-search/search.html')
def search_app():
try:
with open('templates/xdcc-search/search.html', 'r') as dynamic:
contents = dynamic.read()
except:
pass
contents.replace("<file>","MY_FILENAME.tar")
resp = make_response(render_template_string(contents), 200)
no_cache(resp)
return resp
But this is still not using Flask templates the way they are meant to be used. The point of templates is that they should contain things in curly brackets to specify what changes should be made. Replacing a static string inside one with an explicit call to replace bypasses that and does a more primitive version of the same thing.
What you really should be doing is changing your template so that instead of having <file> inside, it, it has {{ file }}, and then you can replace all of that messy view code with this:
#app.route('/xdcc-search/search.html')
def search_app():
resp = make_response(render_template("xdcc-search/search.html", file="MY_FILENAME.tar"), 200)
no_cache(resp)
return resp
Finally, I'm not sure that you need that no_cache, as view functions are not cached by default. Also, the default status code on a response is 200. So probably all you need is this:
#app.route('/xdcc-search/search.html')
def search_app():
return render_template("xdcc-search/search.html", file="MY_FILENAME.tar")

Throw Exception When Parameter Missing Django Rest Framework

I am new to Python, Django and the Django Rest Framework - although I am loving the learning curve!
I would like to know what is the standard (most common) way of raising an exception when an API's parameter is not provided ?
Obviously if conditions in the view's body is not the way to go. Are there an decorators that I can pass parameter names to ?
# urls.py
urlpatterns = [
url(r'test', test),
url(r'errand/make', errand.make),
url(r'errand/preview', errand.preview)
]
# views/errand.py
#api_view(['GET'])
#renderer_classes((JSONRenderer, ))
def preview(request):
e = Errand.objects.get(pk=request.GET['errand_id'])
return Response({'data': e.get_preview_data()})
In order for this line
e = Errand.objects.get(pk=request.GET['errand_id'])
To run fine, errand_id needs to be available. How can I check for certain request keys ?
You'll get a TypeError on any function call where a parameter does not have a default value.
As long as you aren't passing in defaults, you'll get an exception, even if the parameter is never used inside the function.
In your example, since you use dict.get, by default if the key is not found it will return None
If you wanted an exception there, you could try directly accessing the key, which would result in a KeyError when it isn't found. Like so:
...
e = Errand.objects.get(pk=request['errand_id'])
...
Otherwise another solution would be to create a schema that represents the desired structure of the request, and validate the request either in the function or using a decorator.
Something like this would be a start:
def errand_id_required(func):
def func_wrapper(request):
if not request.get('errand_id', False):
raise KeyError('errand_id not present in request')
return func(request)
return func_wrapper
#errand_id_required
def preview(request):
e = Errand.objects.get(pk=request.GET['errand_id'])
return Response({'data': e.get_preview_data()})
I would generally only do this if I had a sophisticated way of validating dictionary schemas, otherwise it's very much overkill to write a decorator to check a single key.
You could try the schema library on pypi, and define something like this:
import schema
errand_schema = {
schema.Optional('some_key'): str,
'id': int,
'errand_id': int,
}
Errand = schema.Schema(errand_schema, ignore_extra_keys=True)
and you could use Errand.validate(request) instead of the if.. raise KeyError that I put in the decorator.
But I'll leave that up to you to decide upon...

How to handle a post request in Flask that contains a list

What i want is to handle an order placed to a server from an android app.
To test things i am using POSTMAN.
I tested it
with the following code.
class newOrder(Resource):
'''
this class receives credentials from a post request
and returns the full menu of the establishment.
'''
def post(self):
try:
parser = reqparse.RequestParser()
parser.add_argument('username', type=str, help='Password to create user')
parser.add_argument('password', type=str, help='Email address to create user')
args = parser.parse_args()
_userUsername = args['username']
_userPassword = args['password']
return jsonify({'username':_userUsername,'password':_userPassword})
except Exception as e:
return {'error': str(e)}
and got this.
So far so good
how can i alter this to work and be tested on POSTMAN and return the following?
{'usename':'foo','password':'bar',
"order":
[
[1,"ΚΡΗΤΙΚΗ",5,,'tt'],
[2,"ΣΑΛΑΤΑ","ΦΑΚΗ",6,'tt'],
[3,"ΣΑΛΑΤΑ","ΚΟΥΣ-ΚΟΥΣ",5,'tt'],
]
}
i am having trouble with the list. 'order':[[],[],[],...]
How to i enter that list to POSTMAN parameters?
Also, i am returning it to my self to simply view it. i just want to know that the data was entered properly.
Thank you.
You need to come up with a format in which to send the list. I believe the most common format is just a comma seperated string like so
thing1,thing2,thing3,etc
Pass this in the url like
https://127.0.0.1:3000/postendpoint?arrayParameter=thing1,thing2,thing3,etc
Then on the python end parse it like this
arrayParameter=args['arrayParameter'].split(',')
Obviously you'll have to make sure the format is correct though
edit:
If you want to pass a sorta multidimentional array then maybe you need to re-evaluate the way your program works. If you really want to do it then try this.
str = 'a,;1;2;3;4;,c,d'
str = str.split(',')
str[1] = str[1].split(';')

Flask, how to jsonify ONLY when return to browser

I try to build an RESTful api server with Flask, and create some functions that can return JSON format data to browser.
Now I hope to make the code more re-usable, for example:
#simple_page.route('/raw_data')
def raw_data():
# to json
pass
#simple_page.route('/score')
def score():
data = raw_data()
# some calculation & return the score (to json)
pass
If there any way in Flask that the function raw_data() returns json format result if and only if the result will be sent back to browser? (Something like #cherrypy.tools.json_out() does in cherrypy)
Thanks in advance.
Factor out producing the raw_data() into a separate function, reused by both routes:
def _produce_raw_data():
return raw_data
#simple_page.route('/raw_data')
def raw_data():
return jsonify(_produce_raw_data())
#simple_page.route('/score')
def score():
data = _produce_raw_data()
# some calculation & return the score (to json)
return jsonify(calculation_results)

Categories