From charlesreid1

No edit summary
 
(27 intermediate revisions by the same user not shown)
Line 1: Line 1:
<!--
=Basics=
Related: [[Making a Chemical Reactor into an Internet of Things Thing]]


=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:
 
<pre>
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)
</pre>
 
If host and port are not specified, flask will only listen on the local network interface on port 5000.
 
<pre>
if __name__ == '__main__':
    app.run(debug=True)
</pre>
 
==Routes==
 
===Simple hello world route===
 
A simple hello world route:
 
<pre>
from flask import Flask
 
...
 
@app.route('/hello')
def hello():
    return "Hello, World!"
</pre>
 
===Returning HTML===
 
To return HTML, we can return it in a string:
 
<pre>
from flask import Flask
 
...
 
@app.route('/get_html')
def get_html():
    return "<h2>Flask Returns HTML Code</h2>"
</pre>
 
===Returning JSON===
 
If we are writing an API, we want routes that return JSON. we can use Flask's jsonify function:
 
<pre>
from flask import Flask, jsonify
 
...
 
@app.route('/list')
def list():
    d = {'a' : 1, 'b' : 2, 'c' : 3}
    return jsonify(d)
</pre>


The Flask documentation is useful for making your first baby step, but it often leaves out the details of what your HTML templates should look like, which is critical information, and therefore a baffling omission.
===Rendering templates===


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


I had some issues with Flask-WTF for creating forms in Flask, mainly because the documentation did not give any information at all about what goes into the HTML template.
<pre>
from flask import Flask, render_template


Between [http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iii-web-forms Flask Mega Tutorial: Web Forms] and [http://stackoverflow.com/questions/14589393/how-to-actually-upload-a-file-using-flask-wtf-filefield Stack Overflow: Uploading a file with Flask WTF], I was able to figure out how to get a Flask web form displaying properly.
...
-->


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


==Deploying Flask Web App as Submodule==
The contents of <code>sample.html</code> are given below:


Instructions for deploying a Flask web app as a submodule in a Python module:
<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>


First, your directory structure will look something like this:
===Raising errors===
 
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:


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


To create an instance of your module's web app from anywhere, follow these steps:
...


1. Install the module
class InvalidUsage(Exception):


2. Import the webapp submodule:
...
 
@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    return response
</pre>
 
 
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 <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
 
=Resources=
 
==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=


More information: http://www.plankandwhittle.com/packaging-a-flask-web-app/
[[Category:API]]
[[Category:Python]]
[[Category:Web]]
[[Category:Web Server]]
[[Category:REST API]]


Yet more information: http://flask.pocoo.org/docs/patterns/distribute/
[[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