Wilson Mar bio photo

Wilson Mar

Hello!

Calendar YouTube Github

LinkedIn

Step-by-step using the Flask REST API library using SQLite3 locally in venv on a Mac

US (English)   Norsk (Norwegian)   Español (Spanish)   Français (French)   Deutsch (German)   Italiano   Português   Estonian   اَلْعَرَبِيَّةُ (Egypt Arabic)   Napali   中文 (简体) Chinese (Simplified)   日本語 Japanese   한국어 Korean

Overview

Flask is called a “micro-framework” because (unlike Python Django and its analogue Ruby on Rails) it provides only what is necessary to do core web development, leaving you to add plug-ins beyond its minimal subset. Flask’s approach keeps your code and workflow simple, particularly on smaller projects.

Unlike Django, which has security built-in, extensions are added to Flask to enable database access, web form, user authentication, payments, etc.

This is a hands-on walkthrough of the intricacies of creating a sample Python server program to process REST API calls from a user’s browser. By hands-on I mean explanations are provided after you do each action.

Installation

You can install Flask on your local Mac laptop (covered below).

But full-stack developer Nick Janetakis (@nickjanetakis), who also created a course on Docker, presents his Docker Compose image of a sample (dice-rolling betting game) application. Nick explains how you can build the 4,000+ lines of “production-quality bullet-proof” source code yourself in his 22-hour video course “Build a SaaS (web) Application with Flask” (bsawf) at $11.99 Udemy.com and marketed (for $59) at buildasaasappwithflask.com and Reddit. BTW Nick also produces the podcast Running in Production.

The latest version of the completed app is periodically updated by the author at
https://github.com/nickjj/build-a-saas-app-with-flask.git

Unique to this website is a bash shell script I’ve written that enables you to install and run it on your MacOS laptop with a single command.

  1. View my install script at:

    https://raw.githubusercontent.com/wilsonmar/DevSecOps/master/bash/install-bsawf.sh
    

    Each feature of the script is explained in my blog article on bash install scripts. My scripts makes use of “feature flags” specified in calling parameters.

  2. To execute the script yourself, first put it in your Clipboard by triple-clicking “bash” in this command, then select copy:

    bash -c "$(curl -fsSL https://raw.githubusercontent.com/wilsonmar/DevSecOps/master/bash/install-bsawf.sh)" -v -E -I -a -o
    

    Connect to the internet if you see:

    curl: (6) Could not resolve host: raw.githubusercontent.com
  3. Open a Terminal on your terminal, click on the cursor, and keypress command+V to paste from Clipboard:

    docker-compose installs these instances:

    • worker_1
    • celery_1
    • redis_1
    • progres_1

    The script ends with a message like these:

    worker_1   | [202X-mm-dd 04:59:42,036: INFO/Beat] beat: Starting...
    

    -o in parameters to the command asks the script to open the app in your default browser (Safari usually on a Mac):

    snakeeyes-landing-899x355.jpg

Technologies used in the app

PROTIP: Nick’s videos present not a deep dive of Flask, but the technologies around Flask. To be “awesome”, the app makes use of several (free) open source packages and code libraries, listed below in alphabetical order:

  1. Ajax requests
  2. Babel JavaScript compiler for compatibility.
  3. Bash script*
  4. Bootstrap vendor JavaScript downloaded
  5. Celery - a background worker for Python to manage UI for long-running work, including scheduled work. Celery uses Redis in-memory datastore to use as a data cache, message broker.
  6. Click - CLI
  7. CSRF (Cross-Site Request Forgery) Protection (see OWASP)
  8. distutils.util (in config/settings.py)
  9. Docker Compose
  10. ES6 JS
  11. Flake8 to analyze Python code (in cli/commands/cmd_flake8.py)
  12. Flask
  13. Flask-debugtoolbar
  14. Flask extensions http://flask.pocoo.org/docs/0.12/extensiondev/
  15. Flask-Mail to send templated emails (in lib/flask_mailplus.py written by Nick)
  16. Flask-Static-Digest
  17. Flask-webpack at https://github.com/nickjj/flask-webpack/blob/master/flask_webpack/init.py
  18. Flask-WTF - Web Forms
  19. WTForms-Components
  20. Fontawesome
  21. Gunicorn - application server for both development and production
  22. Jinga2 templating language for Python
  23. JQuery (not ReactJs) used by Bootstrap
  24. JSON format files
  25. NodeJs
  26. PostgreSQL persistant store
  27. Pytest - package
  28. pytest-cov - code coverage
  29. Redis - cache database
  30. Scss
  31. SQLAlchemy - an ORM to query data in PostgreSQL database
  32. Stripe microtransaction payments for subscriptions and coupon detection. It uses RESTFUL APIs.
  33. Yarn task runner
  34. Webpack (assets/webpack.config.js)
  35. Werkzeug

    Flask is like a glue that sticks together two popular frameworks. Within the scope of MVC (Model-View-Controller) architecture, Werkzeug covers the Controller (C) and Jinja2 covers the View (V).

    Werkzeug at werkzeug.pocoo.org is from a German word for “tool” (for HTTP and routing). Werkzeug is an abstraction of WSGI (Web Server Gateway Interface) for simple and universal interface between web servers and Python web applications. It includes a URL routing system, fully featured request and response objects and a powerful debugger. Also a PEP standard. It’s alternative (used by Instagram) is gunicorn.

    Jinja2 at jinja.pocoo.org is a full-feature template engine for Python, used for replace templating variables code with actual text values at run-time.

    Flask does not provide an integrated Model (M) layer, and lets you pick your database solution. A popular choice is Flask-SQLAlchemy with a ORM (Object-Relational Mapper) over a relational database such as MySQL or PostgreSQL.

    Additionally, Core Flask itself references these Python libraries:

    • aniso8601
    • click (for CLI)
    • Markupsafe
    • flask-restful
    • flask-jsonpify

    Other Python apps in other Udemy Flask classes also reference other libraries in their requirements.txt.

    Not Features

    The sample app lags behind bleeding edge technologies in several areas:

    • Django
    • ReactJs (instead of jQuery) front-end UI coding
    • CSS Flexbox
    • Offline-first UI (with Web Workers)

    • End-to-end (Selenium) UI tests of the app

    • Multi-tenancy in cloud for high-volume usage
    • Paypal payments
    • SSO (Single Sign On) via Facebook, GitHub, Twitter, etc.

    • RESTful APIs. However, Nick created a separate VIDEO about building RESTful APIs.
    • GraphQL
    • OpenAPI (Swagger)

    • Ads

    • JAM static websites

    Taking the video course

    The course’s marketing materials promises to show “the real (battle-hardened production) way (to create the app), without tedious research”. That means the videos walks through the app in various stages of development.

  36. Once you have paid and view the course videos, the Downloading the course materials provides the download link.
  37. Unzip
  38. In Finder view the folders containing app code in various stages of completion:

    1. 06-creating-a-base-flask-app
    2. 07-blueprints-and-jinja-2-templates
    3. 08-testing-and-code-quality
    4. 09-creating-a-cli-script
    5. 10-using-our-first-flask-extension
    6. 11-creating-a-contact-form

    7. 12-creating-a-complete-user-system
    8. 13-creating-a-custom-admin-dashboard
    9. 14-logging-middleware-and-error-handling
    10. 15-quality-of-life-cli-improvements
    11. 16-accepting-recurring-payments
    12. 17-building-the-snake-eyes-game
    13. 18-processing-microtransactions
    14. 19-database-migrations
    15. 20-internationalization-i18n

    Each folder contains a more finished set of files than the previous folder.

    Videos for each lesson provide a walk-through of the code above.

    Coding challenges at the end of each lesson describe the changes you make to each folder.

    PROTIP: The version in the course was the way things were several versions back. Since then, Nick has created several updates (May 2018, April, August, Oct, Dec of 2019). Update videos are included in the total count of videos for the course. This is a very valuable approach as you can see the progression of underlying library changes affecting app code over time.

  39. Activate each lesson (to see the UI) by navigating to the folder to build Docker:

    docker-compose up --build
  40. Install Sublime Text to follow the videos because that’s the text editor (command subl) used. A Productive Linux Development Environment (WSL) on Windows with WSL, Docker, tmux, VSCode and More Dec 21, 2018 [19:32] Developing on Windows with WSL2 (Subsystem for Linux), VS Code, Docker, and the Terminal by Scott Hanselman

  41. Click the wheel icon to ensure that each video is presented in “1080p”.

    snakeeyes-config-148x254

  42. At time of this writing, the “06-creating-a-base-flask-app”

    snakeeyes-obsolete

    App file tree

  43. Open another Terminal to run tree to see all the folders and files to the finished app, presented here because in the course videos fonts are too small to see clearly.

    ├── Dockerfile
    ├── LICENSE
    ├── README.md
    ├── SnakeEyes_CLI.egg-info
    │   ├── PKG-INFO
    │   ├── SOURCES.txt
    │   ├── dependency_links.txt
    │   ├── entry_points.txt
    │   ├── requires.txt
    │   └── top_level.txt
    ├── assets
    │   ├── app
    │   │   ├── app.js
    │   │   ├── app.scss
    │   │   ├── modules
    │   │   │   └── bootstrap.js
    │   │   └── scss
    │   │       ├── _forms.scss
    │   │       ├── _nav.scss
    │   │       ├── _sticky-footer.scss
    │   │       ├── _typography.scss
    │   │       ├── _variables-bootstrap.scss
    │   │       ├── _variables.scss
    │   │       └── all.scss
    │   ├── package.json
    │   ├── postcss.config.js
    │   ├── static
    │   │   ├── 502.html
    │   │   ├── images
    │   │   │   └── snake-eyes.jpg
    │   │   ├── maintenance.html
    │   │   └── robots.txt
    │   ├── webpack.config.js
    │   └── yarn.lock
    ├── celerybeat-schedule
    ├── cli
    │   ├── __init__.py
    │   ├── cli.py
    │   └── commands
    │       ├── __init__.py
    │       ├── cmd_cov.py
    │       ├── cmd_flake8.py
    │       └── cmd_test.py
    ├── config
    │   ├── __init__.py
    │   ├── gunicorn.py
    │   └── settings.py
    ├── docker-compose.override.example.yml
    ├── docker-compose.override.yml
    ├── docker-compose.yml
    ├── docker-entrypoint.sh
    ├── lib
    │   ├── __init__.py
    │   ├── flask_mailplus.py
    │   └── tests.py
    ├── public
    │   ├── 502.html
    │   ├── css
    │   │   └── app.css
    │   ├── fonts
    │   │   ├── fa-regular-400.eot
    │   │   ├── fa-regular-400.svg
    │   │   ├── fa-regular-400.ttf
    │   │   ├── fa-regular-400.woff
    │   │   ├── fa-regular-400.woff2
    │   │   ├── fa-solid-900.eot
    │   │   ├── fa-solid-900.svg
    │   │   ├── fa-solid-900.ttf
    │   │   ├── fa-solid-900.woff
    │   │   └── fa-solid-900.woff2
    │   ├── images
    │   │   └── snake-eyes.jpg
    │   ├── js
    │   │   └── app.js
    │   ├── maintenance.html
    │   └── robots.txt
    ├── requirements.txt
    ├── setup.py
    └── snakeeyes
     ├── __init__.py
     ├── app.py
     ├── blueprints
     │   ├── __init__.py
     │   ├── admin 4000 ???
     │   ├── billing ???
     │   ├── user ???
     │   ├── contact
     │   │   ├── __init__.py
     │   │   ├── forms.py
     │   │   ├── tasks.py
     │   │   ├── templates
     │   │   │   └── contact
     │   │   │       ├── index.html
     │   │   │       └── mail
     │   │   │           └── index.txt
     │   │   └── views.py
     │   └── page
     │       ├── __init__.py
     │       ├── templates
     │       │   └── page
     │       │       ├── home.html
     │       │       ├── privacy.html
     │       │       └── terms.html
     │       └── views.py
     ├── extensions.py
     ├── templates
     │   ├── layouts
     │   │   └── base.html
     │   └── macros
     │       ├── flash.html
     │       └── form.html
     └── tests
         ├── __init__.py
         ├── conftest.py
         ├── contact
         │   ├── __init__.py
         │   ├── test_tasks.py
         │   └── test_views.py
         └── page
             ├── __init__.py
             └── test_views.py
     
    26 directories, 70 files
    

    There is no instance folder containing settings.py used to set MAIL_USEARNAME and MAIL_PASSWORD.

    init.py” are also removed for easier reading.

  44. View the updates video: 3 Upgrading a Dockerized Flask App from Python 2.7 to Python 3.7.4 August 2019. 30:06

    originally python:2.7-slim from DockerHub

  45. Unit Testing (using Pytest)

  46. CLI Script to “help manage your project” in cli/cli.py

  47. Web Sockets*

    VIDEO: Building RESTful APIs with Flask: Visualizing the Application and Library Choices

    App Features

    VIDEO playlist: What Does This Course Cover?

    VIDEO: Build a SAAS App with Flask: Going over the Demo App That We’ll Build


Manually install Flask on MacOS

Actions include folder navigation and creation, virtualenv, etc.

  1. Open a Terminal shell window on Mac or cmd window on Windows.

  2. Create a folder where you hold various projects under your user home folder. On a Mac:

    cd ~ && mkdir -p gits
    

    Alternately, use the workspace you use with a text editor or IDE.

  3. Create and/or navigate into a folder holding where git will create repositories (substituting “wilsonmar” below with your GitHub account name):

    mkdir wilsonmar && cd wilsonmar 
    

    The example here is a user account name on GitHub.com. The repository of interest is at:

    https://github.com/wilsonmar/python-api-flask

    Alternately, you can Fork to your own account and change the URL accordingly.

    You can download a Zip file containing it and unzip it locally. But using Git also downloads a database containing the history of changes.

    Note the repository was forked from GitHub user “sagaragarwal94” = Sagar Chand Agarwal (@sagaragarwal94).

  4. Have Git create a folder and download code from GitHub:

    git clone https://github.com/wilsonmar/python-api-flask
    cd python-api-flask
    

    View code

  5. In a text editor open file server.py

    #!/usr/bin/python3

    means that we need to install the Python interpreter and environment. At version 3, print() functions are used.

    from flask import Flask, request, jsonify
    from flask_restful import Resource, Api
    from sqlalchemy import create_engine
    from json import dumps
       

    The above specifies install of libraries to provide utility functions used in the program.

    db_connect = create_engine('sqlite:///chinook.db')

    means that we need to install the sqlite3 database engine.

    class Employees(Resource):

    means that we need to use the sample database file chinook.db, which contains an employee table.

    Visual Environment

    PROTIP: The standup.sh file in the repo from GitHub performs the steps below automatically. The script can be run repeatedly.

  6. Install virtualenv.
  7. Create a simple virtual environment folder named “venv”:

    virtualenv venv
    cd venv
    

    The response:

    Using base prefix '/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7'
    New python executable in /Users/wilson_mar/gits/wilsonmar/python-api-flask/venv/bin/python3.7
    Also creating executable in /Users/wilson_mar/gits/wilsonmar/python-api-flask/venv/bin/python
    Installing setuptools, pip, wheel...
    done.
    

    PROTIP: The virtualenv program executes commands in the .env file every time we cd into the directory. An example:

    source venv/bin/activate
    export FLASK_APP="server.py"
    export APP_SETTINGS="development"
    export DATABASE_URL="postgresql://localhost/flask_api"
    

    PROTIP: venv is specified in the .gitignore file so the folder isn’t uploaded up to GitHub, since it’s created each time.

  8. Manually activate if you do not use the .env file:

    source venv/bin/activate
    

    The response adds an additional prompt:

    (venv)
    
  9. When the virtual environment is active:

    python --version

    The python interpreter responds that it automatically recognizes python as using Python3, such as:

    Python 3.7.5
  10. BTW, to get out of a virtualenv environment:

    deactivate
    

    Alternately, to get out of an Anaconda environment:

    source deactivate
    

    “(env)” should disappear after this.

    Dependencies in Requirements.txt

  11. Install dependencies within venv:

    pip install flask flask-jsonpify flask-sqlalchemy flask-restful
    

    CAUTION: You did not cd into venv if the response begins with anything other than:

    Collecting flask
      Using cached https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl
    Processing /Users/wilson_mar/Library/Caches/pip/wheels/ea/a9/40/ac47ad604861c1a40499042d30c22cdb7d1fa1abf426597788/Flask_Jsonpify-1.5.0-cp37-none-any.whl
    Collecting flask-sqlalchemy
      Using cached https://files.pythonhosted.org/packages/1e/65/226d95466c75e34e291a76890ed0e27af2e46ab913002847856f11d4d59d/Flask_SQLAlchemy-2.4.1-py2.py3-none-any.whl
    Collecting flask-restful
      Using cached https://files.pythonhosted.org/packages/17/44/6e490150ee443ca81d5f88b61bb4bbb133d44d75b0b716ebe92489508da4/Flask_RESTful-0.3.7-py2.py3-none-any.whl
    

    BTW The repository contains a requirements.txt file listing dependencies which was generated by:

    pip freeze > requirements.txt
    

    The file contains:

    aniso8601==1.3.0
    click==6.7
    Flask==0.12.2
    Flask-Jsonpify==1.5.0
    Flask-RESTful==0.3.6
    Flask-SQLAlchemy==2.3.0
    itsdangerous==0.24
    Jinja2==2.9.6
    MarkupSafe==1.0
    python-dateutil==2.6.1
    pytz==2017.2
    six==1.11.0
    SQLAlchemy==1.1.14
    Werkzeug==0.12.2
    
  12. Download and install Python dependencies specified:

    pip install -r requirements.txt
    
    Collecting aniso8601==1.2.0
      Downloading https://files.pythonhosted.org/packages/5b/fb/251a0dd2f4710e60664ddd8bd3485bd8362530f47af9e88f4061fe589ebf/aniso8601-1.2.0.tar.gz (59kB)
      |████████████████████████████████| 61kB 440kB/s
    Collecting appdirs==1.4.0
      Downloading https://files.pythonhosted.org/packages/7b/8b/eebc6e2002a1e0383f1c7108d0111d4d33ea93bf417d7e19e43ec9b87b2b/appdirs-1.4.0-py2.py3-none-any.whl
    Collecting click==6.7
      Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB)
      |████████████████████████████████| 71kB 920kB/s
    Collecting Flask==0.12
      Downloading https://files.pythonhosted.org/packages/0e/e9/37ee66dde483dceefe45bb5e92b387f990d4f097df40c400cf816dcebaa4/Flask-0.12-py2.py3-none-any.whl (82kB)
      |████████████████████████████████| 92kB 971kB/s
    Collecting Flask-Jsonpify==1.5.0
      Using cached https://files.pythonhosted.org/packages/60/0f/c389dea3988bffbe32c1a667989914b1cc0bce31b338c8da844d5e42b503/Flask-Jsonpify-1.5.0.tar.gz
    Collecting Flask-RESTful==0.3.5
      Downloading https://files.pythonhosted.org/packages/15/2e/41485f3d74103412266f3ce07675adf4bd5b4da16f5f3599b2d114374631/Flask_RESTful-0.3.5-py2.py3-none-any.whl
    Collecting Flask-SQLAlchemy==2.1
      Downloading https://files.pythonhosted.org/packages/b3/52/227aaf4e8cebb153e239c518a9e916590b2fe0e4350e6b02d92b546b69b7/Flask-SQLAlchemy-2.1.tar.gz (95kB)
      |████████████████████████████████| 102kB 1.1MB/s
    Collecting itsdangerous==0.24
      Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB)
      |████████████████████████████████| 51kB 762kB/s
    Collecting Jinja2==2.9.5
      Downloading https://files.pythonhosted.org/packages/3c/d1/49d69bc23d0e0c7612248dd8f5391bd043648865132309616c280ca1c837/Jinja2-2.9.5-py2.py3-none-any.whl (340kB)
      |████████████████████████████████| 348kB 942kB/s
    Collecting MarkupSafe==0.23
      Downloading https://files.pythonhosted.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz
    Collecting packaging==16.8
      Downloading https://files.pythonhosted.org/packages/87/1b/c39b7c65b5612812b83d6cab7ef2885eac9f6beb0b7b8a7071a186aea3b1/packaging-16.8-py2.py3-none-any.whl
    Collecting pyparsing==2.1.10
      Downloading https://files.pythonhosted.org/packages/2b/f7/e5a178fc3ea4118a0edce2a8d51fc14e680c745cf4162e4285b437c43c94/pyparsing-2.1.10-py2.py3-none-any.whl (56kB)
      |████████████████████████████████| 61kB 2.3MB/s
    Collecting python-dateutil==2.6.0
      Downloading https://files.pythonhosted.org/packages/40/8b/275015d7a9ec293cf1bbf55433258fbc9d0711890a7f6dc538bac7b86bce/python_dateutil-2.6.0-py2.py3-none-any.whl (194kB)
      |████████████████████████████████| 194kB 636kB/s
    Collecting pytz==2016.10
      Downloading https://files.pythonhosted.org/packages/f5/fa/4a9aefc206aa49a4b5e0a72f013df1f471b4714cdbe6d78f0134feeeecdb/pytz-2016.10-py2.py3-none-any.whl (483kB)
      |████████████████████████████████| 491kB 977kB/s
    Collecting six==1.10.0
      Downloading https://files.pythonhosted.org/packages/c8/0a/b6723e1bc4c516cb687841499455a8505b44607ab535be01091c0f24f079/six-1.10.0-py2.py3-none-any.whl
    Collecting SQLAlchemy==1.1.5
      Downloading https://files.pythonhosted.org/packages/da/04/8048a5075d6e29235bbd6f1ea092a38dbe2630c670e73d4aa923a4e5521c/SQLAlchemy-1.1.5.tar.gz (5.1MB)
      |████████████████████████████████| 5.1MB 551kB/s
    Collecting Werkzeug==0.11.15
      Downloading https://files.pythonhosted.org/packages/ef/c6/3c431fea5f93c8bc869ec9c7bdad9ffef4ff9c81bfe1d294217447206c46/Werkzeug-0.11.15-py2.py3-none-any.whl (307kB)
      |████████████████████████████████| 317kB 676kB/s
    Building wheels for collected packages: aniso8601, Flask-Jsonpify, Flask-SQLAlchemy, itsdangerous, MarkupSafe, SQLAlchemy
      Building wheel for aniso8601 (setup.py) ... done
      Created wheel for aniso8601: filename=aniso8601-1.2.0-cp27-none-any.whl size=16351 sha256=7277a9c4da02bd09635775afa67967dcf1e5352ad441fb425def7a5930337548
      Stored in directory: /Users/wilson_mar/Library/Caches/pip/wheels/03/22/40/9b2f558eaa39d530e792583795d55365b67efb4299d0dbd5c7
      Building wheel for Flask-Jsonpify (setup.py) ... done
      Created wheel for Flask-Jsonpify: filename=Flask_Jsonpify-1.5.0-cp27-none-any.whl size=3080 sha256=108c18d63b5c289212bb733f9a0d1681dc14460276ecf99260aba3da22e40553
      Stored in directory: /Users/wilson_mar/Library/Caches/pip/wheels/ea/a9/40/ac47ad604861c1a40499042d30c22cdb7d1fa1abf426597788
      Building wheel for Flask-SQLAlchemy (setup.py) ... done
      Created wheel for Flask-SQLAlchemy: filename=Flask_SQLAlchemy-2.1-cp27-none-any.whl size=13440 sha256=f1c2a2a9755f6e28ce2fec9bcb9ebc1a85751850cbe8500a8637684454f4bcfe
      Stored in directory: /Users/wilson_mar/Library/Caches/pip/wheels/bd/18/c4/5b1ebaec15e2bb933089576ebf905ea29976b2f37ed629e1c3
      Building wheel for itsdangerous (setup.py) ... done
      Created wheel for itsdangerous: filename=itsdangerous-0.24-cp27-none-any.whl size=10623 sha256=ac156c8d22f46e9c01e6ec15e141d34565f256805bd50f09b5120c72be73f498
      Stored in directory: /Users/wilson_mar/Library/Caches/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5
      Building wheel for MarkupSafe (setup.py) ... done
      Created wheel for MarkupSafe: filename=MarkupSafe-0.23-cp27-cp27m-macosx_10_14_intel.whl size=17219 sha256=5356a8af98f85fbb6e48c28b84ce37062fe04b2161b369450ff6b9baa52b5ffe
      Stored in directory: /Users/wilson_mar/Library/Caches/pip/wheels/28/de/65/f28b426d990edb591113e1549c8a0f09034e5958e440629306
      Building wheel for SQLAlchemy (setup.py) ... done
      Created wheel for SQLAlchemy: filename=SQLAlchemy-1.1.5-cp27-cp27m-macosx_10_14_intel.whl size=1037499 sha256=2d7a333a5d66c004a607bb5a605f0aa3b706831af82161eca1eb236d18314b32
      Stored in directory: /Users/wilson_mar/Library/Caches/pip/wheels/9b/a4/89/5faed97aa81a384fd97c900b51c964ee28ba4bccd569e51607
    Successfully built aniso8601 Flask-Jsonpify Flask-SQLAlchemy itsdangerous MarkupSafe SQLAlchemy
    ERROR: matplotlib 1.3.1 requires nose, which is not installed.
    ERROR: matplotlib 1.3.1 requires tornado, which is not installed.
    Installing collected packages: six, python-dateutil, aniso8601, appdirs, click, itsdangerous, Werkzeug, MarkupSafe, Jinja2, Flask, Flask-Jsonpify, pytz, Flask-RESTful, SQLAlchemy, Flask-SQLAlchemy, pyparsing, packaging
      Found existing installation: six 1.4.1
    ERROR: Cannot uninstall 'six'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.
    

    Flask home page

  13. Read the description the Flask Framework at http://flask.pocoo.org

    Additional information about the Flask framework is at:

    • http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world
    • https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask

    Core Flask makes use of these libraries:

    • aniso8601
    • click
    • Jinja2
    • MarkupSafe
    • Flask
    • Flash-Jsonify
    • Flask-SQLAlchemy is a Flask extension to support SQLAlchemy, an Object Relational Mapper (ORM).
    • Flask-Restful
    • Werkzeug

    Alternately, a more robust database is to run (on default port 5432):

    Establish Database

  14. Install SQLite3 on MacOS from the internet:

    brew install sqlite3
    

    This installs in another folder (not the pwd).

    The chinook.db is in the repository. But if it’s not there, download the “chinook.zip” file from:

    http://www.sqlitetutorial.net/download/sqlite-sample-database/?wpdmdl=94

    1. Unzip file to obtain file “chinook.db” which contains the database.
    2. Move the chinook.db file to the python_rest folder.

  15. Open the database using SQLite:

    sqlite3  chinook.db  &
    

    The response contains the date of the SQLite3 version being used:

    SQLite version 3.24.0 2018-06-04 14:10:15
    Enter ".help" for usage hints.
    sqlite> 
    
  16. Open another Terminal to list commands:

     sqlite> .help
    

    The response is as shown on https://www.sqlite.org/cli.html

    .auth ON|OFF           Show authorizer callbacks
    .backup ?DB? FILE      Backup DB (default "main") to FILE
    .bail on|off           Stop after hitting an error.  Default OFF
    .binary on|off         Turn binary output on or off.  Default OFF
    .changes on|off        Show number of rows changed by SQL
    .check GLOB            Fail if output since .testcase does not match
    .clone NEWDB           Clone data into NEWDB from the existing database
    .databases             List names and files of attached databases
    .dbinfo ?DB?           Show status information about the database
    .dump ?TABLE? ...      Dump the database in an SQL text format
                          If TABLE specified, only dump tables matching
                          LIKE pattern TABLE.
    .echo on|off           Turn command echo on or off
    .eqp on|off|full       Enable or disable automatic EXPLAIN QUERY PLAN
    .exit                  Exit this program
    .explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic
    .fullschema ?--indent? Show schema and the content of sqlite_stat tables
    .headers on|off        Turn display of headers on or off
    .help                  Show this message
    .import FILE TABLE     Import data from FILE into TABLE
    .imposter INDEX TABLE  Create imposter table TABLE on index INDEX
    .indexes ?TABLE?       Show names of all indexes
                          If TABLE specified, only show indexes for tables
                          matching LIKE pattern TABLE.
    .limit ?LIMIT? ?VAL?   Display or change the value of an SQLITE_LIMIT
    .log FILE|off          Turn logging on or off.  FILE can be stderr/stdout
    .mode MODE ?TABLE?     Set output mode where MODE is one of:
                          ascii    Columns/rows delimited by 0x1F and 0x1E
                          csv      Comma-separated values
                          column   Left-aligned columns.  (See .width)
                          html     HTML <table> code
                          insert   SQL insert statements for TABLE
                          line     One value per line
                          list     Values delimited by .separator strings
                          quote    Escape answers as for SQL
                          tabs     Tab-separated values
                          tcl      TCL list elements
    .nullvalue STRING      Use STRING in place of NULL values
    .once FILENAME         Output for the next SQL command only to FILENAME
    .open ?--new? ?FILE?   Close existing database and reopen FILE
                          The --new starts with an empty file
    .output ?FILENAME?     Send output to FILENAME or stdout
    .print STRING...       Print literal STRING
    .prompt MAIN CONTINUE  Replace the standard prompts
    .quit                  Exit this program
    .read FILENAME         Execute SQL in FILENAME
    .restore ?DB? FILE     Restore content of DB (default "main") from FILE
    .save FILE             Write in-memory database into FILE
    .scanstats on|off      Turn sqlite3_stmt_scanstatus() metrics on or off
    .schema ?PATTERN?      Show the CREATE statements matching PATTERN
                           Add --indent for pretty-printing
    .separator COL ?ROW?   Change the column separator and optionally the row
                          separator for both the output mode and .import
    .shell CMD ARGS...     Run CMD ARGS... in a system shell
    .show                  Show the current values for various settings
    .stats ?on|off?        Show stats or turn stats on or off
    .system CMD ARGS...    Run CMD ARGS... in a system shell
    .tables ?TABLE?        List names of tables
                          If TABLE specified, only list tables matching
                          LIKE pattern TABLE.
    .testcase NAME         Begin redirecting output to 'testcase-out.txt'
    .timeout MS            Try opening locked tables for MS milliseconds
    .timer on|off          Turn SQL timer on or off
    .trace FILE|off        Output each SQL statement as it is run
    .vfsinfo ?AUX?         Information about the top-level VFS
    .vfslist               List all available VFSes
    .vfsname ?AUX?         Print the name of the VFS stack
    .width NUM1 NUM2 ...   Set column widths for "column" mode
                          Negative values right-justify
     
  17. List the 11 custom data tables in the sample chinook database loaded:

     sqlite> .tables
    

    The response:

    albums          employees       invoices        playlists
    artists         genres          media_types     tracks
    customers       invoice_items   playlist_track
    

    PROTIP: These tables are a rather strange assortment that normally do not belong together. But it’s there as technical samples.

    sqlite-sample-database-color-650x327-82797

  18. For more information from the SQLite Tutorial website.

    • employees table stores employees data such as employee id, last name, first name, etc. It also has a field named ReportsTo to specify who reports to whom.
    • customers table stores customers data.
    • invoices & invoice_items tables: these two tables store invoice data. The invoices table stores invoice header data and the invoice_items table stores the invoice line items data.
    • artists table stores artists data. It is a simple table that contains only artist id and name.
    • albums table stores data about a list of tracks. Each album belongs to one artist. However, one artist may have multiple albums.
    • media_types table stores media types such as MPEG audio file, ACC audio file, etc.
    • genres table stores music types such as rock, jazz, metal, etc.
    • tracks table store the data of songs. Each track belongs to one album.
    • playlists & playlist_track tables: playlists table store data about playlists. Each playlist contains a list of tracks. Each track may belong to multiple playlists. The relationship between the playlists table and tracks table is many-to-many. The playlist_track table is used to reflect this relationship.

    invoke

  19. Open another Terminal shell window so that the database remains running.
  20. Navigate to the directory you are using:

    cd ~/gits/wilsonmar/python-api-flask
    
  21. Ensure permissions: On a Mac:

    chmod a+x server.py
    
  22. Initiate the Python web service:

    python server.py
    

    Alternately:

    ./server.py
    

    If you get this response, you propably forgot to run requirements.txt:

    Traceback (most recent call last):
      File "server.py", line 2, in <module>
     from flask import Flask, request, jsonify
    ImportError: No module named flask

    The response expected:

    * Serving Flask app "server" (lazy loading)
            * Environment: production
    WARNING: This is a development server. Do not use it in a production deployment.
    Use a production WSGI server instead.
            * Debug mode: off
            * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    

    Run Flask Compile and Preview

  23. Define the environment variable recognized by Flask and run flask:

    export FLASK_APP=server.py
    
  24. Compile using Flask

    flask run
    

    Alternately:

    python -m flask run
    
  25. In a browser visit the web page on the designated port:

    open https://localhost:5000/tracks
    

    What you see depends on the JSON handler on your internet browser.

    python-flask-response-687x272

  26. If you are following along Tom Bell’s projects on Pluralsight, see his preview at:

    https://pfjb.thomasbell.org

    Discussions with Tom Bell

    Routes

    Routing incoming URL to Python code that handles them is at the heart of what Flask does.

    The default root is defined with this code:

    @app.route('/')
    def index():
        """
        Render a hello world response.
        :return: Flask response
        """
        return 'Hello World!'
    

    The three double-quotes encasing the docstring:

    Tests Walkthough

    Tests are coded first in a BDD (Behavior Driven Design):

  27. Open tests.py in a text editor.
  28. View the bottom of the program where code to invoke it is defined:

    if __name__ == '__main__':
      app.run
    

    Routes Walkthough

  29. Open server.py in a text editor.
  30. View the bottom of the program where code to invoke it is defined:

    if __name__ == '__main__':
      app.run
    

    Alternately, app.run(debug=True).

    Up from the bottom of the program are three api.add_resource statements that define routes the server processes.

    Note how modular each route is. This enables incremental addition of programming code capabilities over time.

    Get Tracks

  31. In your browser, go to the URL with “tracks”:

    http://127.0.0.1:5000/tracks

    Alternately, on the command line:

    curl -i http://127.0.0.1:5000/tracks
    

    The response (JSON file) shows details for each media track:

    {
      "data": [
     {
       "Composer": "Angus Young, Malcolm Young, Brian Johnson", 
       "Name": "For Those About To Rock (We Salute You)", 
       "TrackId": 1, 
       "UnitPrice": 0.99
     }, 
    
  32. In the Terminal/Console, notice the detailed logging:

    127.0.0.1 - - [09/Dec/2019 21:10:32] "GET / HTTP/1.1" 404 -
    127.0.0.1 - - [09/Dec/2019 21:10:32] "GET /favicon.ico HTTP/1.1" 404 -
    127.0.0.1 - - [09/Dec/2019 21:12:20] "GET /tracks HTTP/1.1" 200 -
    

Unit testing

  1. Flask comes with unit testing support. To run tests defined in file test_module1.py within the tests folder:

    pytest -k module1

Review outputs

  1. Review this output from a database query to obtain a result returned:

    class Tracks(Resource):
     def get(self):
         conn = db_connect.connect()
         query = conn.execute("select trackid, name, composer, unitprice from tracks;")
         result = {'data': [dict(zip(tuple (query.keys()) ,i)) for i in query.cursor]}
         return jsonify(result)
    

    Note each class is self-contained.

    The conn object is defined before making the query.

    The execute function sends to the database connection SQL commands referencing fields defined in the database schema shown above.

    The query.cursor points to the whole query response object. The for keyword loops the various keys, using i as the index of each current item.

    tuple identifies the key in each item in the query object.

    zip takes one or more sequences, and returns a sequence of tuples.

    dict constructs dictionary objects from a sequence of key/value tuples.

    data is the high-level item in the response.

    The jsonify function formats the result object to JSON formatting for display.

    List Employees

  2. In your browser, go to the URL accessing the “employees” list route:

    http://127.0.0.1:5000/employees

    Alternately, on the command line:

    curl -i http://127.0.0.1:5000/employees
    

    The response shows ids of all the employees in the pre-populated database. All 8 in the sample database:

    {"employees": [1, 2, 3, 4, 5, 6, 7, 8]}
    

    TODO: Now we change the code to list employee names instead of just their numbers.

    List specific employee

  3. In your browser, go to the URL accessing the employees/item route:

    http://127.0.0.1:5000/employees/8

    Alternately, on the command line:

    curl -i http://127.0.0.1:5000/employees/8
    

    Either way, this response shows details of employee whose employeeid is 8:

    {
      "data": [
     {
       "Address": "923 7 ST NW", 
       "BirthDate": "1968-01-09 00:00:00", 
       "City": "Lethbridge", 
       "Country": "Canada", 
       "Email": "laura@chinookcorp.com", 
       "EmployeeId": 8, 
       "Fax": "+1 (403) 467-8772", 
       "FirstName": "Laura", 
       "HireDate": "2004-03-04 00:00:00", 
       "LastName": "Callahan", 
       "Phone": "+1 (403) 467-3351", 
       "PostalCode": "T1H 1Y8", 
       "ReportsTo": 6, 
       "State": "AB", 
       "Title": "IT Staff"
     }
      ]
    }
    

    The output output is from this code which makes a database query:

    class Employees(Resource):
     def get(self):
         conn = db_connect.connect() # connect to database
         query = conn.execute("select * from employees") # This line performs query and returns json result
         return {'employees': [i[0] for i in query.cursor.fetchall()]} # Fetches first column that is Employee ID
    
  4. Instead of “8”, use annother number to obtain data for another employee.

    PROTIP: This is no longer considered a secure design. Services now use hashed values instead of allowing incrementable numbers to identify specific rows.

    Post new employee

    PROTIP: The previous code defines the response to “get” requests.

    The code to post a new entry is this:

    def post(self):
         conn = db_connect.connect()
         print(request.json)  # used during testing.
         LastName = request.json['LastName']
         FirstName = request.json['FirstName']
         Title = request.json['Title']
         ReportsTo = request.json['ReportsTo']
         BirthDate = request.json['BirthDate']
         HireDate = request.json['HireDate']
         Address = request.json['Address']
         City = request.json['City']
         State = request.json['State']
         Country = request.json['Country']
         PostalCode = request.json['PostalCode']
         Phone = request.json['Phone']
         Fax = request.json['Fax']
         Email = request.json['Email']
         query = conn.execute("insert into employees values(null,'{0}','{1}','{2}','{3}', \
                              '{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}', \
                              '{13}')".format(LastName,FirstName,Title,
                              ReportsTo, BirthDate, HireDate, Address,
                              City, State, Country, PostalCode, Phone, Fax,
                              Email))
         return {'status':'success'}
    

    def post(self): appears instead of a get in the definition.

    Unlike Get requests, which involve specification of just an URL, post requests also require the client to submit to the server additional fields in the HTTP header.

    The Python server program expects from the client to add into the database with this coding:

    LastName = request.json['LastName']

    If a curl utility program is used as the client, it would look like this:

    curl -X POST http://127.0.0.1:5000/employees/9 -d '{"LastName":"Wayne", "FirstName":"Bruce"}' -H "Content-Type: application/json"
    

    PROTIP: The simple sample code contains no editing of inputs which all “production worthy” code should have. Examples of edits include whether all required fields are supplied and that content are valid. Such edits would be in client software as well.

    The content following -d provides what is specified in server code such as:

    LastName = request.json['LastName']

    The application/json specifes the format expected back from the server.

    PROTIP: This starter program does not have logic to prevent duplicates from being added.

Next

TODO: Generation of code from database using Swagger (OpenAPI).

TODO: Lint code with default SonarQube rules.

TODO: GraphQL - https://github.com/graphql-python/flask-graphql or after buiding schemas using http://graphene-python.org/

TODO: Add HATEOS to respond to URL with no resource (folder) specified.

Flask provides “blueprints” to organize code into packages for a more modular architecture. A Flask Blueprint is like an application component – not an application itself, but can be registered in an application. Each Blueprint lives in its own folder.

f5-sdk for Python

Jason Rahm’s Getting started with F5 Networks® BIG-IP® iControl® REST interface at https://github.com/F5Networks/f5-common-python with docs at https://f5-sdk.readthedocs.io/en/latest/

  1. f5-common-python SDK 27-Jul-2017

  2. unnamed resources and commands 24-Aug-2017

  3. working with statistics 07-Jun-2018

  4. working with request parameters 14-Jun-2018

  5. request parameters revisited 22-Jun-2018

  6. transactions 13-Sep-2018

Resources

This tutorial was described at https://www.codementor.io/sagaragarwal94/building-a-basic-restful-api-in-python-58k02xsiq

https://scotch.io/tutorials/build-a-restful-api-with-flask-the-tdd-way

http://python.apichecklist.com/ from Dan Bader.

https://www.patricksoftwareblog.com/steps-for-starting-a-new-flask-project-using-python3/ FEBRUARY 19, 2018

Pluralsight has several video tutorials on Flask:

Tom Bell’s projects at Pluralsight.com (pluralsight-projects) have videos along with step-by-step instructions and checking of work your in GitHub (like Code School before purchase by Pluralsight):

  • “Add Authentication to a Flask CMS” checks on whether you’ve forked from https://github.com/pluralsight-projects/PythonFlask-CMSAuthentication, etc. The project involves managing session variables, clear and get session data, custom route decorators. Initially, all the tests fail. The course covers fixing the tests. The solution is at another branch: https://github.com/wilsonmar/PythonFlask-CMSAuthentication/tree/module1-solution

  • “Refactor a Flask CMS” 1hr Oct 28, 2019 shows use of an Admin Blueprint by creating and editing a route. https://github.com/pluralsight-projects/PythonFlask-CMSBlueprints

  • Build a Job Board with Python & Flask 2h 35m on Sep 26, 2018 forks from https://github.com/pluralsight-projects/PythonFlask-JobBoard

How to build a web app using Python’s Flask and Google App Engine” 5 Nov 2018. It uses the OpenWeather API.

  • https://www.codeastar.com/easy-python-weather-forecast-tool/ uses the geopy python module and https://darksky.net/dev API in https://github.com/codeastar/weather_on_trip from 2017

  • https://www.codeastar.com/easy-accuweather-forecast-in-python/ using tinydb by https://github.com/codeastar/ez_accuweather_python

API projects

https://dev.to/aligoren/using-elasticsearch-with-python-and-flask-2i0e for Debian

Sample front-end projects

https://www.youtube.com/watch?v=YW8VG_U-m48 Serving React with a Flask Backend (using PyCharm, Yarn)

  • https://subscription.packtpub.com/book/web_development/9781785881114/8/ch08lvl1sec45/reactjs-and-flask book on Packt By Tarek Ziadé July 2017

  • https://medium.com/@neilvodoor/using-react-w-flask-82d8341876e4

  • https://www.codeastar.com/react-frontend-weather-forecast-2/

  • https://developer.okta.com/blog/2018/12/20/crud-app-with-python-flask-react

https://testdriven.io/blog/developing-a-single-page-app-with-flask-and-vuejs/

https://github.com/alexdebrie/serverless-flask

https://stackabuse.com/serving-static-files-with-flask/

Cisco PSIRT

The Product Security Incident Response Team (PSIRT) at Cisco has open-sourced:

    https://github.com/CiscoPSIRT/openVulnAPI

The openVuln API is a RESTful API for technical staff and programmers to build tools that to keep up with security vulnerability information. It uses open security standards OVAL, CVRF, CVSS, CVE, CWE

  1. Obtain a Cisco account
  2. Sign in at

    https://apiconsole.cisco.com

  3. Review posts in the PSIRT Community

  4. Register Your Application with selected APIs to receive your OAuth2.0 credentials.
  5. Get Access Tokens for your application using

    https://id.cisco.com/oauth2/default/v1/token

  6. Store Access Tokens securely (such as in Vault)

  7. Read about the latest changes:

    https://raw.githubusercontent.com/api-at-cisco/Images/master/Whats_New_Doc.pdf

  8. Interactive try the “Hello World” API manually using Postman GUI or curl CLI at

    https://apiconsole.cisco.com/io-docs

    NOTE: Token URL: https://cloudsso.cisco.com/as/token.oauth2

    https://api.cisco.com/security/advisories/v2/all

    interaction with https://api.cisco.com, see:

  9. Click the download icon to download JSON output.

  10. View all API endpoints at:

https://developer.cisco.com/docs/psirt/#!api-reference/api-reference

REMEMBER: Limitations:

  • Endpoint /all is removed due to the amount of data that it returns.
  • Requests that result in large amount of data being returned may hang/timeout.
  1. Code your app to make API Calls using your token and get access to your data.

  2. Contact the developer by posting an issue.

https://apiconsole.cisco.com/docs/read/overview/Platform_Introduction

API

VIDEO: John Dupuy on ashcrow’s flask-track-usage and flash-analytics Flask extensions:* provides flexibility by having the app control logs using MongoEngine (MongoDB) and PostgreSQL for storage. (read documentation)

Internationalization

To internationalize the app, add Flask-Babel==0.9 to the requirements.txt file. It was created by the same author as Flask itself.

References

VIDEO: “Flask for Fun and Profit” by Armin Ronacher

Building REST APIs with Python by Wayne Mary is based on PostgreSQL and Django (not Flask).

Continuous API Management Copyright O’Reilly 2019 by Mehdi Medjaoui, Erik Wilde, Mitra Pandey Consulting, Ltd., and Amundsen.com

Deploy a Flask app on AWS EC2

More about Python

This is one of a series about Python:

  1. Python install on MacOS
  2. Python install on MacOS using Pyenv
  3. Python install on Raspberry Pi for IoT

  4. Python tutorials
  5. Python Examples
  6. Python coding notes
  7. Pulumi controls cloud using Python, etc.
  8. Jupyter Notebooks provide commentary to Python

  9. Python certifications

  10. Test Python using Pytest BDD Selenium framework
  11. Test Python using Robot testing framework
  12. Testing AI uses Python code

  13. Microsoft Azure Machine Learning makes use of Python

  14. Python REST API programming using the Flask library
  15. Python coding for AWS Lambda Serverless programming
  16. Streamlit visualization framework powered by Python
  17. Web scraping using Scrapy, powered by Python
  18. Neo4j graph databases accessed from Python