# Python : Pallets : Flask
\[ [pypi](https://pypi.org/project/Flask/) | [src](https://github.com/pallets/flask/) | [docs](https://flask.palletsprojects.com/en/3.0.x/) | [api](https://flask.palletsprojects.com/en/3.0.x/api/) ]
The core is a thin wrapper around [[Werkzeug]] – for example, Flask’s `Request` and `Response` classes subclass from Werkzeug while adding/changing very little. It also makes use of [[Click]], [[Jinja]], and [[MarkupSafe]].
###### Hello World
```python
app = Flask( __name__ )
@app.route( "/<name>" )
def hello( name ):
return f"Hello { markupsafe.escape( name ) }!""
```
```bash
flask [--app <app>] run [--debug] # can omit --app if file is "app.py" or "wsgi.py"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
```
###### Imports
```python
from flask import...
Flask, # app
current_app, g, request, session, # context globals
abort, has_app_context, has_request_context, jsonify, # utilities
make_response, redirect, render_template, send_file, url_for
from flask.views import...
View, MethodView # view classes
```
###### Globals
- look like globals, but are unique to your context (based on Werkzeug’s [Context Locals](https://werkzeug.palletsprojects.com/en/latest/local/) which are based on Python’s [contextvars](https://docs.python.org/3/library/contextvars.html))
- require app and/or request contexts to have been “pushed”
```python
current_app # currently active app
request # currently active request
g # namespace object for storing data for the life of the app context
# (good place to stash a User object or DB session)
session # currently active session
# utilities
abort( <code>, description="..." ) # raise HTTPException for given status code
has_app_context() # bool (whether an app context is currently "pushed")
has_request_context() # bool (whether a request context is currently "pushed")
redirect( location, code=<code> ) # create redirect response object
render_template()
send_file()
```
#### App API
```python
app.config # subclass of dict
app.url_for( <name>, **values )
app.url_map
```
###### Modes
Two ways to set, both referencing a shared value.
```python
app.config[ "DEBUG" ] -or- app.debug # reloader, interactive debugger
app.config[ "TESTING" ] -or- app.testing # exceptions propogated
```
#### Routes
```python
"/"
"/<name>" # variable (passed to view function as args)
"/<int:id>" # variable with converter; converters:
string (default) accepts any text without a slash
int accepts positive integers
float accepts positive floating point values
path like string but also accepts slashes
uuid accepts UUID strings
# re: Trailing Slashes
"/foo/" # "/foo" will redirect to "/foo/"
"/foo" # "/foo/" will produce a 404
# By default, a route only answers to GET requests.
# GET gives you HEAD and OPTIONS for free.
@app.route( "/login", methods=[ "GET", "POST" ] )
@app.get( "/login" )
@app.post( "/login" )
# alternative to decorators
app.add_url_rule( "/", view_func=home )
app.add_url_rule( "/other", view_func=other )
```
###### Reversing
```python
url_for( "<func_name>", var1=, ..., qparam1=, ... )
url_for( ".<func_name>" ) # reference view in same Blueprint
url_for( "<blueprint>.<func_name>" ) # reference view in different Blueprint
Examples:
url_for( "index" ) -> "/"
url_for( "login", next="/" ) -> "/login?next=/"
```
#### Request & Response
See [[Werkzeug]] for Request and Response APIs.
###### Return Values
Normally you return data and let Flask turn it into a Response object. If you want to do that yourself:
```python
response = make_response( content, code ) # takes same kinds of args you can return from a View
```
Otherwise:
- string -> treated as response body with 200 code and “text/html” mimetype
- dict/list -> `jsonify()` called to produce a response
- iterator/generator returning strings or bytes -> treated as a streaming response
- tuples ->
```python
( response, status )
( response, headers )
( response, status, headers )
```
- otherwise, assumes return value is a valid WSGI app
#### View Classes
###### View
```python
class MyView( View ):
init_every_request = False # default is True, which creates new instance on each request
decorators = [ ... ] # equiv of dec2( dec1( view ) ) )
methods = [ "GET", ... ]
def __init__( self, ...iargs... ):
...
def dispatch_request( self, ...pargs... ):
...
app.add_url_rule( "/foo/", view_func=MyView.as_view( "foo", ...iargs... ) )
```
- iargs -> instantiation args
- pargs -> path args
###### MethodView
```python
MethodView < View
class MyView( MethodView ):
def __init__( self, ...iargs... ):
...
def <method>( self, ... ):
...
```
#### Blueprints
Flask and Blueprint both inherit from Scaffold, which contains the route methods.
```python
foo = Blueprint( "foo", __name__, ...options... )
@foo.route( ... )
def whatever( ... ):
...
app.register_blueprint( foo, ...options... )
subdomain = "..."
url_defaults = { ... }
url_prefix = "..."
```
#### Misc
###### Event Handlers
```python
@app.before_request # for setup such as establishing a DB conn
@app.after_request # for modifying responses
@app.teardown_request # for cleanup and error-handling
```
###### Logging
```python
app.logger.debug( "A value for debugging" )
app.logger.warning( "A warning occurred (%d apples)", 42 )
app.logger.error( "An error occurred' )
```
###### Sessions
```python
app.secret_key = b"_5#y2L"F4Q8z\n\xec]/" # python -c 'import secrets; print(secrets.token_hex())'
if "username" in session:
return f"Logged in as { session[ "username" ] }"
session[ "username" ] = request.form[ "username" ]
session.pop( "username", None )
```
###### Static Files
For support during development:
- create a folder called `static` in your package; it will be available at `/static`
- to generate URLs, use the special `static` name: `url_for( "static", filename="style.css" )`
###### WSGI Middleware
```python
app.wsgi_app = MyWSGIMiddleware( app.wsgi_app )
```
## Testing
#### Classes
```python
flask.testing.EnvironBuilder < werkzeug.test.EnvironBuilder
flask.testing.FlaskClient < werkzeug.test.Client
werkzeug.test.TestResponse
```
#### Test Client
```python
client = app.test_client()
response = client.<method>(
query_string={ "key": "value", ... }
headers={}
data= # body for POST, PUT
json={} # Content-Type header will be set to application/json automatically
follow_redirects=True
)
response.data # bytes
response.text # string
response.json
response.history # if following redirects
```
###### Body Input Types
- bytes
- dict -> `Content-Type` will be `multipart/form-data` or `application/x-www-form-urlencoded`
- open file
#### Fixtures
Assuming [[Tech/Python/Testing/pytest/Overview]].
###### Simplest Version
```python
@pytest.fixture( name="app", scope="session" )
def fixture__app():
return create_app() # your factory function
@pytest.fixture( name="client" )
def fixture__client( app ):
return app.test_client()
```
###### With App Setup and Teardown
```python
@pytest.fixture( name="app", scope="session" )
def fixture__app():
app = create_app() # your factory function
... # setup
yield app
... # teardown
```