Table of Contents:
- Configuration
- Creating a Flask Project
- Data and Objects
- Database Configuration
- Web Forms and Security
- Database Connection
- Data Aggregation
- State Management
- Deployment on Heroku
Configuration
- Ensure you have Python3, pip, and venv
- Self-upgrade pip with
python3 -m pip install --upgrade pip
- Self-upgrade pip with
- Creation of the project folder
- Creation of the virutal environment using
venv
python3 -m venv env
. env/bin/activate
deactivate
to exit the virtual environment
- Install Flask to virtual enviornment
pip3 install flask
- Setup enviornment variables
touch .flaskenv
to create the environment variablespip3 install python-dotenv
will allow enviornment variables to be envoked
- Install the Flask-WTF extension
pip3 install flask-wtf
for web forms within Flask
- Create a
requirements.txt
file for tracking dependenciespip freeze > requirements.txt
pip install -r requirements.txt
if installing on a new device/location
Creating a Flask Project
- Create a
main
module- Instaniate a Flask application object
- Create a default route to display data to the UI
- Run and inspect the application
flask run
- Creating the project structure
- Directories for templates, static data, etc.
- Create an application package
__init__.py
- Refactor the
main.py
module
- Running and configuring the development enviornment
- Create
config.py
androutes.py
- Create
- Creating the home page of the application
- Create the template for
index.html
- Importating the
render_template
function from the Flask module - Usage of the Jinja template expression
- Usage of the
include
directive to include external files
- Create the template for
- Creating navigation menus and routing patterns
- Usage of
url_for
function for resolving links - Usage of
route()
decorator to bind a function to one or more URL patterns- Eg.
https://domain.com/
,https://domain.com/index
,https://domain.com/home
- Eg.
- Usage of Jinga delimiters, eg.
{% ... %}
or{# ... #}
- Usage of
Jinja Templates
- Usage of Jinja’s inheritance logic
- Creating the base template
- Using template inheritance to create child templates
- Passing data to the view using props
- Data from the source to the view
- Using the
For
directive - Building tables with JSON data
- Access data via the request and response objects
- URL variables
- HTTP Methods (GET, POST)
- Global Objects: Request and Response (JSON API Format)
Data and Objects
Request Object:
- Accessing Query String (GET)
request.args.get(<field_name>)
- If you put a get function and you get something from the curly string and, if that ID with that token is not there, then you will get a message saying none, so it doesn’t crash your site.
request.args[<field_name>]
- If you’re using the array method, that means you’re very specific and assuming you will guarantee that that variable has to be there; if it’s not, it’ll crash the site, so you don’t want that as well.
- Accessing Form Data (POST)
request.form.get(<field_name>)
request.form[<field_name>]
Response Object:
The Response object is usually used for creating APIs and Flask has this class of response that gives you six parameters that you can use:
1
2
3
4
5
6
7
8
9
class flask.Response(
# Most commonly used parameters:
response = None,
mimetype = None,
content_type = None,
status = None,
headers = None,
direct_passthrough = False)
URL Variables:
- Routing patterns (Eg.
domain.com/page/variable
) - Creating a URL variable
- Setting default data to a URL variable
- Passing a URL variable to a template
GET Method:
- Create forms using the GET method
- Create the form template
- Create the form route (URL pattern)
- Accesssing form data via the GET method
POST Method:
- Updating the enrollment form using POST method
- Adding the GET and POST methods to route
- Acessing form data using the form object
Database Configuration
See: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/ and https://www.mongodb.com/try/download/compass
- Utilize the MongoDB database system with Flask
sudo systemctl start mongod
mongodb-compass
pip install flask-mongoengine
- Update
requirements.txt
:pip freeze > requirements.txt
- Set up and connect to the new database:
- Import the newly installed MongoEngine:
From flask_mongoengine import MongoEngine
- Set up a MongoDB database:
MONGODB_SETTINGS = { 'db' : 'DATABASE_NAME' }
- Initialize the database object:
db = Mongo Engine(); db.init_app(app)
- Import the newly installed MongoEngine:
- Connecting to the database:
- Connecting to the MongoDB via the MongoEngine object
- Intergrate a user collection using a user model class
- Insert sample user document (data) to a collection
- Display the collection back to the view
- Creating the documents and data:
- Insert documents within the collections
- Use of MongoDB shell commands:
- Create collections (tables):
db.createCollection( <collection> )
- Insert an entry into a collection:
db.<collection>.insert( { ... } )
- Insert multiple entries into a collection:
db.<collection>.insertMany( {...} )
- Create collections (tables):
- Insert JSON data using the
mongoexport.exe
via the command line:mongoimport --db <DB> -collection <collection> --file <file>
- Shortcut version:
mongoimport --d <DB> -c <collection> --file <file>
- Creating the data models:
- Models for each collection (Eg. User, Courses, Enrollment, etc.)
1
2
3
4
5
class ModelName(db.Document):
field1 = db.IntField()
field2 = db.StringField()
# ...
fieldn = db.StringField()
Web Forms and Security
- Install and configure Flask-WTF and Flask-Security extensions:
- Flask-WTF is an extension for the WTForms Library
- WTForms provides a clean way to generate HTML form fields
- In addition, maintains a separation of code and presentation (similar to MVC)
pip install flask-wtf
- Flask-Security provides common security and authentication features:
- Session-bassed authentication
- Password hashing
- Basic HTTP and token-based authentications
- User registration and login tracking (w/ Flask-Login)
- Supports data persistency for Flask-SQLAlchemy, Flask-MongoEngine, flask-peewee, and PonyORM
pip install flask-security
- Flask-WTF is an extension for the WTForms Library
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Example login.html in Jinja:
<form>
# Generates a CSRF token.
</form>
# Example forms.py
class LoginForm(FlaskForm):
email = StringField('Email')
password = StringField('Password')
submit = SubmitField('Login')
- Creating the login and registration pages
- Creation of form classes and updating the templates using the WTForms library
- Creation of alert messages using the
flash()
method (source) - Retrieving flash messages using the
get_flashed_messages()
(view) - Validation of forms and displaying error messages
- Update the login route to capture form data
Database Connection
- Processing form data and updating the database
- Processing form data for database updating with validation
- Update the login and registration route to interact with the database
- Hash passwords using the Werkseug library (A WSGI web app library)
pip install werkzeug
- Hashing: `generate_password_hash(‘password’)
- Unhashing: `check_password_hash(password, ‘password’)
- Inserting data and verifying the database using the Compass interface
- Creation of the courses and enrollment pages
- Querying course data from the database
- Populate the course template with this data
- Perform join queries on muliple collections using
$aggregate
.$lookup
: Performs a left outer join.$match
: Filters documents.$unwind
: Deconstructs an array field
- Important note: When using an online database, whitelist your IP address.
Data Aggregation
- Usage of the MongoDB aggregation framework using the Compass interface
- Creation of the aggregation pipeline to process data in three stages
$lookup
,$match
,$unwind
,$sort
Example Pipeline:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 1. First $lookup Example:
*
* from: The target collection.
* localField: The local join field.
* foreignField: The target join field.
* as: The name for the results.
* pipeline: The pipeline to run on the joined collection.
* let: Optional variables to use in the pipeline field stages.
*/
{
from: 'enrollment',
localField: 'user_id',
foreignField: 'user_id',
as: 'result_1'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 2. First $unwind Example:
*
* path: Path to the array field.
* includeArrayIndex: Optional name for index.
* preserveNullAndEmptyArrays: Optional
* toggle to unwind null and empty values.
*/
{
path: '$result_1',
includeArrayIndex: 'string',
preserveNullAndEmptyArrays: false
}
1
2
3
4
5
6
7
8
9
10
/**
* 3. Second $lookup Example:
*
*/
{
from: 'course',
localField: 'result_1.courseID',
foreignField: 'courseID',
as: 'result_2'
}
1
2
3
4
5
6
7
/**
* 4. Second $unwind Example:
*/
{
path: '$result_2',
preserveNullAndEmptyArrays: false
}
1
2
3
4
5
6
7
/**
* 5. $match Example:
* query: The query in MQL.
*/
{
'user_id': user_id
}
1
2
3
4
5
6
7
/**
* 6. $sort Example:
* Provide any number of field/order pairs.
*/
{
"courseID": 1
}
State Management
- Usage of Flask-Session for state management and user authentication
- The
session
object stores information that’s specific to a user - Implementation is on top of cookies, and signs cookies crpytographically
- Creationg of Logout and Enrollment routes with sessions.
- The
- More secure alternative: Flask-Login extension
- Mangaing user logged-in state using a
user_loader()
function - Using the LoginManager class to manage login state
- Implementing the “remember me” feature
- Restricting access to protected pages with
@login_required
- Logging out users using the
logout_user()
function - See more: https://flask-login.readthedocs.io/en/latest/
- Mangaing user logged-in state using a
- Generate secure secret key from command-line:
python3 -c "import os; print(os.urandom(16))"
1
2
3
4
5
6
7
# UrlSegment via the Parameters Property:
session['key'] = value # Setting a session
session.get('key') # Getting a session
session.pop('key', None) # Destroying a session
session['key'] = False # Destroying a session
Deployment on Heroku
- Flask web frameworks features convenient built-in web servers
- But these blocking servers only process a single request at a time.
- If you deploy with one of these servers on Heroku, your dyno resources will be underutilized and your application will feel unresponsive.
- Gunicorn is a pure-Python HTTP server for WSGI applications.
- It allows you to run any Python application concurrently by running multiple Python processes within a single dyno.
- It provides a perfect balance of performance, flexibility, and configuration simplicity.
pip install gunicorn
- Create an app on Heroku, which prepares Heroku to receive your source code:
heroku create
- Deploy your code:
git push heroku master
- Ensure that at least one instance of the app is running:
heroku ps:scale web=1
- Visit the app at the URL generated by its app name. As a handy shortcut, you can open the website as follows:
heroku open
- View information about your running app using one of the logging commands:
heroku logs --tail
- Deploy your code:
- Use a Procfile, a text file in the root directory of your application, to explicitly declare what command should be executed to start your app.
- The Procfile in the example app you deployed looks like this:
web: gunicorn app:app
- Use the default Procfile by running:
heroku local web
- The Procfile in the example app you deployed looks like this:
- Reinstall your requirements:
pip install -r requirements.txt
Deployment Security
For database security, in app.json
, add the following:
1
2
3
4
5
"env": {
"MONGO_URI": {
"description": "The URI to connect to the MongoDB.",
"value": "mongodb://localhost:27017/FLASK_Enrollment"
},
As well as modifying config.py
:
1
2
3
MONGODB_SETTINGS = { "db" : "FLASK_Enrollment",
"host" : os.environ.get("MONGO_URI")
}
Afterwards, you can modify the value
of MONGO_URI
directly within the settings of Heroku. This prevents the password of the MongoDB admin from being revealed.
Of course, also make sure your virtual environment folder, env
, is also in your .gitignore
.