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
venvpython3 -m venv env. env/bin/activatedeactivateto exit the virtual environment
- Install Flask to virtual enviornment
pip3 install flask
- Setup enviornment variables
touch .flaskenvto create the environment variablespip3 install python-dotenvwill allow enviornment variables to be envoked
- Install the Flask-WTF extension
pip3 install flask-wtffor web forms within Flask
- Create a
requirements.txtfile for tracking dependenciespip freeze > requirements.txtpip install -r requirements.txtif installing on a new device/location
Creating a Flask Project
- Create a
mainmodule- 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.pymodule
- Running and configuring the development enviornment
- Create
config.pyandroutes.py
- Create
- Creating the home page of the application
- Create the template for
index.html - Importating the
render_templatefunction from the Flask module - Usage of the Jinja template expression
- Usage of the
includedirective to include external files
- Create the template for
- Creating navigation menus and routing patterns
- Usage of
url_forfunction 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
Fordirective - 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 mongodmongodb-compasspip 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.exevia 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
sessionobject 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.