From charlesreid1

No edit summary
 
(20 intermediate revisions by the same user not shown)
Line 1: Line 1:
=Basics=
=Basics=


==Flask 101==
==Basic App==


The Flask mega tutorial is really handy: http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world
Run a Flask app that is bound to all network interfaces on the host, and listens on port 5000:


=RESTful API=
<pre>
 
We can use Flask to design and implement a RESTful API service. Flask will be a server and interpret between URLs and Python actions. Note that this is particularly handy if you're designing an internet-of-things thing, as you can install Flask on a [[RaspberryPi]], and now you have a RESTful API for accessing your Raspberry Pi! If you take it a step further, you can control the GPIO pins on the Raspberry Pi, and make the Pi do things with your hardware and circuitry based on your API actions.
 
==Hello World==
 
Let's put our Flask hello world app in <code>run.py</code>:
 
<source lang="python">
from flask import Flask
from flask import Flask


Line 22: Line 14:
     return "Hello, World!"
     return "Hello, World!"


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
</pre>
If host and port are not specified, flask will only listen on the local network interface on port 5000.
<pre>
if __name__ == '__main__':
if __name__ == '__main__':
     app.run(debug=True)
     app.run(debug=True)
</source>
</pre>


now we run it with <code>python run.py</code>
==Routes==


==Return Some Data with JSON==
===Simple hello world route===


Now let's return some data from the RESTful server to the client (perhaps a sensor on a Raspberry Pi running the RESTful API):
A simple hello world route:


<source lang="python">
<pre>
from flask import Flask, jsonify
from flask import Flask


app = Flask(__name__)
...


tasks = [
@app.route('/hello')
    {
def hello():
        'id': 1,
     return "Hello, World!"
        'title': u'Buy groceries',
</pre>
        'description': u'Milk, Cheese, Pizza, Fruit, Tylenol',
        'done': False
    },
    {
        'id': 2,
        'title': u'Learn Python',
        'description': u'Need to find a good Python tutorial on the web',
        'done': False
    }
]
 
@app.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():
     return jsonify({'tasks': tasks})


if __name__ == '__main__':
===Returning HTML===
    app.run(debug=True)
</source>


Now you can request that resource using CURL:
To return HTML, we can return it in a string:


<pre>
<pre>
$ $ curl -i http://localhost:5000/todo/api/v1.0/tasks
from flask import Flask
HTTP/1.0 200 OK
 
Content-Type: application/json
...
Content-Length: 294
Server: Werkzeug/0.8.3 Python/2.7.3
Date: Mon, 20 May 2013 04:53:53 GMT


{
@app.route('/get_html')
  "tasks": [
def get_html():
    {
     return "<h2>Flask Returns HTML Code</h2>"
      "description": "Milk, Cheese, Pizza, Fruit, Tylenol",
      "done": false,
      "id": 1,
      "title": "Buy groceries"
    },
     {
      "description": "Need to find a good Python tutorial on the web",
      "done": false,
      "id": 2,
      "title": "Learn Python"
    }
  ]
}
</pre>
</pre>


==Send Some Data with POST==
===Returning JSON===


You can also send some data with POST requests:
If we are writing an API, we want routes that return JSON. we can use Flask's jsonify function:


<source lang="python">
<pre>
from flask import request
from flask import Flask, jsonify
 
...
 
@app.route('/list')
def list():
    d = {'a' : 1, 'b' : 2, 'c' : 3}
    return jsonify(d)
</pre>


@app.route('/todo/api/v1.0/tasks', methods=['POST'])
===Rendering templates===
def create_task():
    if not request.json or not 'title' in request.json:
        abort(400)
    task = {
        'id': tasks[-1]['id'] + 1,
        'title': request.json['title'],
        'description': request.json.get('description', ""),
        'done': False
    }
    tasks.append(task)
    return jsonify({'task': task}), 201
</source>


and the corresponding output when we POST data to the server:
This example shows how to take an integer variable from the URL and use it on an HTML template page:


<pre>
<pre>
$ curl -i -H "Content-Type: application/json" -X POST -d '{"title":"Read a book"}' http://localhost:5000/todo/api/v1.0/tasks
from flask import Flask, render_template
HTTP/1.0 201 Created
Content-Type: application/json
Content-Length: 104
Server: Werkzeug/0.8.3 Python/2.7.3
Date: Mon, 20 May 2013 05:56:21 GMT


{
...
  "task": {
 
    "description": "",
@app.route('/sample/<int:sample_number>')
    "done": false,
def sample(sample_number):
     "id": 3,
     render_template('sample.html', sample_number=sample_number)
    "title": "Read a book"
  }
}
</pre>
</pre>


=Packaging Flask Apps=
The contents of <code>sample.html</code> are given below:


==Deploying Flask Web App as Submodule==
<pre>
    <html>
    <head>
    <title>Sample {{sample_number}}</title>
    </head>
    <body>
        <h2>{{sample_number}}</h2>
        <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula.</p>
    </body>
    </html>
</pre>


Instructions for deploying a Flask web app as a submodule in a Python module:
===Raising errors===


First, your directory structure will look something like this:
To abort with a 400 (server error), use the abort function


<pre>
<pre>
README.md
from flask import Flask, abort
setup.py
 
mymodule/
...
  __init__.py
 
  submodule1/
@app.route('/how_to_abort')
  submodule2/
def how_to_abort():
  webapp/
    import random
     __init__.py
     if random.random() < 0.5:
    additional_routes.py
        abort(400)
     templates/
     else:
      [...]
        return "<h2>Hi there!</h2>"
    static/
      [...]
</pre>
</pre>


Next, your setup.py file will look something like this:
===Exceptions and errors===
 
To write a custom exception that will return <code>abort(YYY)</code> (where YYY is an HTTP code), you can write an exception class that defines a status_code private variable:


<pre>
<pre>
config = {
class InvalidUsage(Exception):
     'description': 'My Module',
     status_code = 400
    'install_requires': ['flask'],
 
     'packages': ['mymodule','mymodule.submodule1','mymodule.submodule2','mymodule.webapp'],
     def __init__(self, message, status_code=None, payload=None):
    'include_package_data' : True,
         Exception.__init__(self)
    'package_data' : {
         self.message = message
         'templates' : 'mymodule/webapp/templates/*',
         if status_code is not None:
         'static' : 'mymodule/webapp/static/*'
            self.status_code = status_code
         },
        self.payload = payload
    'scripts': [],
    'name': 'mulch',
    'zip_safe' : False
}


setup(**config)
    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        return rv
</pre>
</pre>


The key lines here are <code>include_package_data</code> and <code>package_data</code>, which will also install your non-Python template and static files with your module.
Next, we need to create a handler for this type of error, and register it with our application:
 
<pre>
from flask import jsonify
 
...
 
class InvalidUsage(Exception):
 
...
 
@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    return response
</pre>


Now you can install your module with <code>python setup.py install</code>, and your module is available to use from anywhere.


To create an instance of your module's web app from anywhere, follow these steps:
Described here in the flask documentation: http://flask.pocoo.org/docs/1.0/patterns/apierrors/


1. Install the module
==POST Endpoints==


2. Import the webapp submodule:
To deal with data sent via a POST request, use the <code>request</code> variable, imported from Flask, and get the POSTed data using <code>request.json</code>:


<pre>
<pre>
from mymodule.webapp import *
from flask import Flask, jsonify, abort, request
 
...
 
@app.route('/post_something',methods=['POST'])
def post_something():
    if not request.json:
        abort(400)
    post_data = request.json
 
    # Now render a template for the user
    # and populate the template variables
    # with data that was POSTed
    render_template('posted.html',
                        title = post_data.get('title','A Pretty Boring Title'),
                        author = post_data.get('author','Anonymous Coward'),
                        color = post_data.get('color','blue'),
    )
 
 
    # NOTE:
    # If you'd rather return json, just do
    # return jsonify({'status':'ok'}), 200
 
</pre>
</pre>


3. Start the webapp:
Now we can POST data to the server by adding the JSON content type to a curl request, and pass data in using the -d flag. here's the curl request:


<pre>
<pre>
app.run(debug=True)
$ curl -i -H "Content-Type: application/json" -X POST -d '{"title":"My first post", "author":"Gladys Overwith", "color":"orange"}' http://localhost:5000/post_something
</pre>
</pre>


Voila!
=Packaging Flask Apps=
 
[[Flask/Packaging]] - a page that covers how to bundle flask apps into a ready-to-distribute Python package


More information: http://www.plankandwhittle.com/packaging-a-flask-web-app/
=Resources=


Yet more information: http://flask.pocoo.org/docs/patterns/distribute/
==Flaskadillo==


I put together a tutorial on how to use Flask. This is available in the flaskadillo repo:
* https://git.charlesreid1.com/charlesreid1/flaskadillo
* https://github.com/charlesreid1/flaskadillo
==Links==
The Flask mega tutorial is really handy: http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world
Micro blog example: https://stormpath.com/blog/build-a-flask-app-in-30-minutes


=Flags=
=Flags=


[[Category:API]]
[[Category:Python]]
[[Category:Web]]
[[Category:Web Server]]
[[Category:REST API]]
[[Category:REST API]]
[[Category:Python Packaging]]

Latest revision as of 17:11, 15 November 2018

Basics

Basic App

Run a Flask app that is bound to all network interfaces on the host, and listens on port 5000:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Hello, World!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

If host and port are not specified, flask will only listen on the local network interface on port 5000.

if __name__ == '__main__':
    app.run(debug=True)

Routes

Simple hello world route

A simple hello world route:

from flask import Flask

...

@app.route('/hello')
def hello():
    return "Hello, World!"

Returning HTML

To return HTML, we can return it in a string:

from flask import Flask

...

@app.route('/get_html')
def get_html():
    return "<h2>Flask Returns HTML Code</h2>"

Returning JSON

If we are writing an API, we want routes that return JSON. we can use Flask's jsonify function:

from flask import Flask, jsonify

...

@app.route('/list')
def list():
    d = {'a' : 1, 'b' : 2, 'c' : 3}
    return jsonify(d)

Rendering templates

This example shows how to take an integer variable from the URL and use it on an HTML template page:

from flask import Flask, render_template

...

@app.route('/sample/<int:sample_number>')
def sample(sample_number):
    render_template('sample.html', sample_number=sample_number)

The contents of sample.html are given below:

    <html>
    <head>
    <title>Sample {{sample_number}}</title>
    </head>
    <body>
        <h2>{{sample_number}}</h2>
        <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula.</p>
    </body>
    </html>

Raising errors

To abort with a 400 (server error), use the abort function

from flask import Flask, abort

...

@app.route('/how_to_abort')
def how_to_abort():
    import random
    if random.random() < 0.5:
        abort(400)
    else:
        return "<h2>Hi there!</h2>"

Exceptions and errors

To write a custom exception that will return abort(YYY) (where YYY is an HTTP code), you can write an exception class that defines a status_code private variable:

class InvalidUsage(Exception):
    status_code = 400

    def __init__(self, message, status_code=None, payload=None):
        Exception.__init__(self)
        self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload

    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        return rv

Next, we need to create a handler for this type of error, and register it with our application:

from flask import jsonify

...

class InvalidUsage(Exception):

...

@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    return response


Described here in the flask documentation: http://flask.pocoo.org/docs/1.0/patterns/apierrors/

POST Endpoints

To deal with data sent via a POST request, use the request variable, imported from Flask, and get the POSTed data using request.json:

from flask import Flask, jsonify, abort, request

...

@app.route('/post_something',methods=['POST'])
def post_something():
    if not request.json:
        abort(400)
    post_data = request.json

    # Now render a template for the user
    # and populate the template variables
    # with data that was POSTed
    render_template('posted.html',
                        title = post_data.get('title','A Pretty Boring Title'),
                        author = post_data.get('author','Anonymous Coward'),
                        color = post_data.get('color','blue'),
    )


    # NOTE:
    # If you'd rather return json, just do
    # return jsonify({'status':'ok'}), 200

Now we can POST data to the server by adding the JSON content type to a curl request, and pass data in using the -d flag. here's the curl request:

$ curl -i -H "Content-Type: application/json" -X POST -d '{"title":"My first post", "author":"Gladys Overwith", "color":"orange"}' http://localhost:5000/post_something

Packaging Flask Apps

Flask/Packaging - a page that covers how to bundle flask apps into a ready-to-distribute Python package

Resources

Flaskadillo

I put together a tutorial on how to use Flask. This is available in the flaskadillo repo:


Links

The Flask mega tutorial is really handy: http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

Micro blog example: https://stormpath.com/blog/build-a-flask-app-in-30-minutes

Flags