CIS 192: Lecture 10 Web Development with Flask Lili Dworkin University of Pennsylvania
Last Week s Quiz req = requests.get("http://httpbin.org/get") 1. type(req.text) 2. type(req.json) 3. type(req.json())
Last Week s Quiz req = requests.get("http://httpbin.org/get") 1. type(req.text) <type unicode > 2. type(req.json) <type instancemethod > 3. type(req.json()) <type dict >
Last Week s Quiz >>> def hi():... return 0... >>> hi <function hi at 0x1003bbc08> >>> hi() 0 >>> type(hi) <type 'function'> >>> type(hi()) <type 'int'>
Last Week s Quiz >>> class Foo():... def bar(self):... return 0... >>> f = Foo() >>> f.bar <bound method Foo.bar of < main.foo instance at 0 x1003caef0>> >>> f.bar() 0 >>> type(f.bar) <type 'instancemethod'> >>> type(f.bar()) <type 'int'>
Last Week s Quiz 1. type(json.dumps(json.loads('{"key": "val"}'))) 2. type(json.loads(json.dumps(0)))
Last Week s Quiz 1. type(json.dumps(json.loads('{"key": "val"}'))) <type str > 2. type(json.loads(json.dumps(0))) <type int >
Last Week s Quiz JSON isn t a type JSON is a format so strings can be valid JSON or not json.dumps Input: Python data structure consisting of dicts, lists, ints, strings, or bools Output: String json.loads Input: String in valid JSON format Output: Python data structure
Last Week s Quiz >>> json.dumps([1, 2, 3]) '[1, 2, 3]' >>> json.dumps(0) '0' >>> json.dumps('a') '"a"' # note the double quotes >>> json.dumps(f) TypeError: < main.foo instance at 0x1003caef0> is not JSON serializable
Last Week s Quiz >>> json.loads('[1, 2, 3]') [1, 2, 3] >>> json.loads('0') 0 >>> json.loads('a') ValueError: No JSON object could be decoded >>> json.loads("a") ValueError: No JSON object could be decoded >>> json.loads('"a"') u'a'
Last Week s Quiz Return the content type of a response object called req.
Last Week s Quiz Return the content type of a response object called req. req.headers[ content-type ]
Last Week s Quiz What is the main difference between GET and POST requests?
Last Week s Quiz What is the main difference between GET and POST requests? GET requests send parameters in the URL. POST requests send parameters in the body.
Last Week s Quiz Why does it matter? The server can see the data regardless of whether it is in the URL or the body. And if someone intercepts your package, so can they. But GET requests remain in browser history and can be cached and bookmarked, so you want to avoid that with sensitive data. If you send information in the headers of your GET request, that s safe (as long as you are over SSL). Point of the story: use GET for retrieving, POST for sending.
Last Week s Quiz l = [lambda x: 1. type(l) 2. l[0](0) x + i for i in range(5)]
Last Week s Quiz l = [lambda x: x + i for i in range(5)] 1. type(l) <type list > (My typo: I really wanted type(l[0]) what is that?) 2. l[0](0) 4 i is a name, and throughout the loop it is being pointed at successively higher integers When we refer to i, we get what it currently points to
Web Frameworks We ve been talking about making HTTP requests What about serving them? Flask is a microframework small and simple, and you can see how and why everything is happening Django is the big guy on the block more fully featured, but also more black magic / mysterious
Hello World from flask import Flask app = Flask( name ) @app.route('/') def hello_world(): return "Hello World!" if name == ' main ': app.run() prompt$ python flask.py * Running on http://127.0.0.1:5000/
Hello World app = Flask( name ) When we create an instance of the Flask class, the first argument is the name of the application s module or package When using a single module, use name because this will work regardless of whether name equals main or the actual import name
Hello World @app.route('/') def hello_world(): return "Hello World!" The app.route('/') decorator tells Flask to call the hello_world() function when the relative url / is accessed The hello_world() function returns the web page (in this case, a simple string) to be displayed
Hello World app.run() The app.run() function runs the application on a local server This will only be visible on your own computer! We will talk about deployment later
Debugging When testing, use app.run(debug=true) Now the server will reload itself on code changes Additionally, you will see error messages in the browser But never leave this on in production!
More Routing @app.route('/bad') def bad(): return 'hi' + 4 @app.route('/bye') def bye_world(): return "Bye World!"
Variable Rules To add variable parts to a url, use <variable_name> The variables are passed as arguments to the function @app.route('/user/<username>') def greet_user(username): return "Hello %s!" % username
Variable Rules Multiple urls can route to the same function: @app.route('/name/<first>') @app.route('/name/<first>/<last>') def greet_name(first, last=none): name = first + ' ' + last if last else first return "Hello %s!" % name
Templating What about some real HTML? Flask uses a templating system called Jinja. <!doctype html> <title>hello from Flask</title> {% if name %} <h1>hello {{ name }}!</h1> {% else %} <h1>hello World!</h1> {% endif %} Need to put this in a templates folder.
Templating from flask import render_template @app.route('/template/') @app.route('/template/<name>') def template(name=none): return render_template('index.html', name=name)
Templating Even cooler... <!doctype html> <title>making a List</title> <ul> {% for item in l %} <li>{{ item }}</li> {% endfor %} </ul> @app.route('/templatelist') def template_list(): l = ['a', 1, True] return render_template('list.html', l=l)
GET Requests Recall: a url can be accessed with parameters, e.g.: /hello?key=value Retrieve these parameters from the request.args dictionary from flask import request @app.route('/args') def args(): html = '' for key, value in request.args.items(): html += '%s=%s' % (key, value) html += '<br/>' return html
GET Requests Even better, using templates: <!doctype html> <title>displaying Params</title> <ul> {% for key, value in params.items() %} <li>{{ key }}={{ value }}</li> {% endfor %} </ul> @app.route('/template_args') def template_args(): return render_template('params.html', params=request.args)
POST Requests We can also make POST requests to a url Add keyword argument methods=['post', 'GET'] to the app.route() decorator Check if a request was a POST by looking at request.method The data from a POST request can be retrieved from the request.form dictionary
POST Requests @app.route('/post', methods=['post', 'GET']) def post(): if request.method == 'POST': return request.form.get('data', 'default') else: return 'That was a GET request.' >>> url = 'http://127.0.0.1:5000/post' >>> req = requests.post(url, data={'data':'test'}) >>> req.text u'test' >>> req = requests.post(url) >>> req.text u'default'
POST Requests What if we try sending a list? >>> url = 'http://127.0.0.1:5000/post' >>> req = requests.post(url, data={'data': [1, 2, 3]}) >>> req.text u'1'
POST Requests Instead, use JSON! Only ever send strings across the wires. @app.route('/postlist', methods=['post']) def post_list(): l = request.form.get('data') l = json.loads(l) return ','.join(map(str, l)) >>> url = 'http://127.0.0.1:5000/postlist' >>> resp = requests.post(url, data={'data': json.dumps([1,2,3])}) >>> resp.text 1,2,3
Returning JSON Instead of returning HTML, what if we want to return JSON? from flask import jsonify @app.route('/json') def return_json(): return jsonify({'some': 'data'}) >>> url = 'http://127.0.0.1:5000/json' >>> req = requests.get(url) >>> req.json() {u'some': u'data'}
Final Project Instructions here.