Sign Up
Hero

Using Python, Flask, and Angular to Build Modern Web Apps - Part 1

In this series, you will learn how to create modern web applications with Python, Flask, and Angular.

TL;DR: In this series, you will learn how to create modern web applications with Python, Flask, and Angular. You will use this stack to build a SPA and a backend API to expose exams and questions so users can test their knowledge regarding different technologies. In this GitHub repository, you can find the final code created throughout the first part of the series.

So far, this series contains three parts:

  1. Part 1 (this one) includes topics like bootstrapping the Flask application, managing Entities with SQLAlchemy ORM, and bootstrapping the Angular application.
  2. Part 2 includes topics like securing Flask Apps, handling Angular forms, and securing Angular Apps.
  3. Part 3 includes topics like configuring Angular Material, handling Authorization, and migrating Databases with Alembic.

What You Will Build

In this series, you will use Python, Flask, and Angular to build a web application based on a modern architecture. With Angular, you will build a SPA (Single Page App) that allows users to browse through exams and questions. These users, when authenticated, will be able to test their knowledge regarding a specific topic by choosing one of the multiple choices that a question exposes. Then, when your users submit their answers, your backend will check if they are right or wrong, record the result, and send back this result to users.

As you are looking forward to building a modern web application, you will use Auth0 as the identity management system of your app. You will also persist all exams, questions, alternatives, and results on a database.

Why Python, Flask, and Angular

As StackOverflow recently analyzed, Python is one of the fastest-growing programming languages, having surpassed even Java on the number of questions asked on the platform. Besides that, the language is also showing mass adoption on GitHub. On this platform, Python occupied the second position on the number of pull requests opened in 2017.

When it comes to developing web applications with Python, you will have to choose between two popular frameworks: Django or Flask. Django is more mature and a little bit more popular than Flask. However, Flask has its strengths too. From the beginning, Flask was built to be scalable and simple to get started with. Applications built with Flask are clearly lighter when compared to Django counterparts. As such, Python developers usually refer to Flask as a microframework.

For the frontend application, you will use Angular as this is one of the most popular frameworks around. To learn about advantages of this framework, you can check this nice page on Rangle.io. As stated by this page, Angular provides developers with the tools needed to build and structure large-scale JavaScript applications. Besides that, Angular has some big advantages over some alternatives. For example, Angular is built and supported by Google engineers. Alongside with these engineers, there is a huge community ready to help you with issues when the time comes.

As you can see, by choosing Python, Flask, and Angular to build web applications, you can rest assured that you will always be able to rely on great and thriving communities to support you.

"Python, Flask, and Angular form a great stack to build modern web applications."

Tweet This

Dependencies

Now that you learned why Python, Flask, and Angular form a great stack to build modern web applications, you are ready to install the local dependencies. This section is divided into two subsections to highlight what are the environment dependencies from the backend and from the frontend perspectives.

Backend Dependencies

To start with, you will need an up to date version of Python 3. If you don't have Python 3 available on your development machine, please, browse to the Python download page and install it.

After installing Python, you will have to install the pipenv tool. This tool aims at bringing the best of all packaging worlds (bundler, composer, npm, etc.) to Python developers. Also, this tool is a first–class citizen on Windows. So, if you are still stuck on this operating system, don't worry, you are covered.

To install pipenv, simply open a terminal and type the following command:

# depending on the environment, you will have to use
# pip3 instead of pip (just once)
pip install pipenv

Python and pipenv together are enough to start developing your Flask application. However, as you want to persist transactional data, you still need to choose and configure a database engine. To make your life easier, you will use SQLAlchemy to persist and retrieve data from the chosen engine. If you don't have experience with SQLAlchemy, please, check this nice introductory article on the subject. There, you will learn that by using the SQLAlchemy ORM (Object Relational Mapping) extension, you will be able to easily connect and use any major SQL database engine (e.g. MySQL, PostgreSQL, SQL Server, etc).

If you don't have a database available on your machine, one great way to proceed is to use Docker to create a new one:

docker run --name online-exam-db \
    -p 5432:5432 \
    -e POSTGRES_DB=online-exam \
    -e POSTGRES_PASSWORD=0NLIN3-ex4m \
    -d postgres

Of course, to run the command above, you will need to have Docker installed locally.

Frontend Dependencies

As you are going to use Angular to create your frontend application, you will need Node.js and NPM installed on your machine. You can install both tools simultaneously by downloading and executing an installer (choose one based on your operating system) from the Node.js download page. Another alternative is to use a tool like NVM to manage multiple active node versions. On a development machine, this is probably the best option.

Whichever installation method you choose, make sure you are using an up to date version of Node.js (i.e. >= 8).

After properly installing Node.js and NPM, you can use the npm command to install the Angular CLI tool. You will use this CLI (Command Line Interface) to bootstrap the frontend app, start a development server, and to create Angular components, services, etc.

Use the following command to install the Angular CLI:

npm install -g @angular/cli

Bootstrapping the Python Application

Now that you have taken care of the environment dependencies, you can focus on developing your application. For starters, you can create a directory to hold all the frontend and the backend source code of your app. Also, you will probably want to commit everything to a Git repository to guarantee that your progress is saved. Therefore, use the following commands to start structuring your app:

# create the project root directory
mkdir online-exam

# move into it
cd online-exam

# initialize it as a Git repository
git init

After that, you will want a directory specifically created to your Flask backend application:

# create directory to hold backend source code
mkdir backend

# move into it
cd backend

Then, you will want to use pipenv to create a virtual environment. If you don't know why you need a virtual environment, check out this great article written by the author of pipenv.

# initialize the virtual environment
pipenv --three

As you are using Git to backup your code, you will probably want to ignore some files. To do this, create a file called .gitignore in the project root directory and copy the rules from this URL into it.

Managing Entities with SQLAlchemy ORM

With your virtual environment set up, you can start developing the features of your application. A good place to start is to define entities and to configure SQLAlchemy to persist and retrieve instances of these entities. As such, you will use pipenv to install the sqlalchemy package and a driver to connect to your database. If you are using PostgreSQL, you can use the psycopg2-binary driver alongside with SQLAlchemy. If you are using another database engine, please, check this page to choose a good driver.

The following command shows how to use pipenv to install sqlalchemy and the psycopg2-binary driver:

# install sqlalchemy and psycopg2 on the venv
pipenv install sqlalchemy psycopg2-binary

After installing SQLAlchemy and a driver to connect to the database, you can start creating your entities. To do so, use the following commands to create a module called entities inside another module called src:

# create directories
mkdir -p src/entities

# create file to mark src as a module
touch src/__init__.py

# create file to mark entities as a module
touch src/entities/__init__.py

# create file to hold some boilerplate code
touch src/entities/entity.py

The first two touch commands above simply create empty __init__.py files to mark both directories as Python modules. The last touch command creates the file that will hold a class called Entity. You will use this class as the superclass to all your entities. This will be useful to avoid having to repeat some boilerplate code to connect to the database and to define some common properties (e.g. id and created_at):

# coding=utf-8

from datetime import datetime
from sqlalchemy import create_engine, Column, String, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

db_url = 'localhost:5432'
db_name = 'online-exam'
db_user = 'postgres'
db_password = '0NLIN3-ex4m'
engine = create_engine(f'postgresql://{db_user}:{db_password}@{db_url}/{db_name}')
Session = sessionmaker(bind=engine)

Base = declarative_base()


class Entity():
    id = Column(Integer, primary_key=True)
    created_at = Column(DateTime)
    updated_at = Column(DateTime)
    last_updated_by = Column(String)

    def __init__(self, created_by):
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
        self.last_updated_by = created_by

Then, after defining the Entity class, you can create a file called exam.py to represent your first entity:

touch src/entities/exam.py

On this file, insert the following code:

# coding=utf-8

from sqlalchemy import Column, String

from .entity import Entity, Base


class Exam(Entity, Base):
    __tablename__ = 'exams'

    title = Column(String)
    description = Column(String)

    def __init__(self, title, description, created_by):
        Entity.__init__(self, created_by)
        self.title = title
        self.description = description

Here, you are defining a class called Exam that inherits from Entity and from Base. This entity contains, besides the properties defined by its superclasses, two properties: title and description. Besides that, this class also defines that instances of it must be persisted to and retrieved from a table called exams.

Having the Exam and Entity classes properly defined, you can create a script called main.py in the src directory to test if they are really connecting to the database:

touch src/main.py

Inside this script, you can add the following code:

# coding=utf-8

from .entities.entity import Session, engine, Base
from .entities.exam import Exam

# generate database schema
Base.metadata.create_all(engine)

# start session
session = Session()

# check for existing data
exams = session.query(Exam).all()

if len(exams) == 0:
    # create and persist mock exam
    python_exam = Exam("SQLAlchemy Exam", "Test your knowledge about SQLAlchemy.", "script")
    session.add(python_exam)
    session.commit()
    session.close()

    # reload exams
    exams = session.query(Exam).all()

# show existing exams
print('### Exams:')
for exam in exams:
    print(f'({exam.id}) {exam.title} - {exam.description}')

As you can see, the code in this script is quite simple. Here is a list that summarizes what it does:

  • It starts by importing Session, engine, and Base from the .entities.entity module.
  • Then, it imports the Exam class from the .entities.exam module.
  • Then, it generates (if needed) the database schema.
  • After generating the schema, it queries all instances of Exam.
  • Then, if there are no exams on the database, it creates a new one and queries all instances of the Exam class again.
  • Lastly, it prints the exams retrieved from the database.

To run this script, you will have to activate the virtual environment (created by pipenv) then use python to trigger the src.main module:

# activate virtual environment
pipenv shell

# run main module
python -m src.main

If everything works as expected, your module will create an instance of Exam, persist to the database, and print its details on the terminal.

By the way, this might be a good time to save your progress:

git add . && git commit -m "adding SQLAlchemy and some entities"

Managing HTTP Requests with Flask

Now that your app is connected to a database, it's time to transform it into a Flask web application. To do so, the first thing you will need is to install Flask. Besides Flask, you will also need to install marshmallow to handle serialization and deserialization of JSON objects. To install both dependencies, issue the following command in the backend directory:

pipenv install flask marshmallow

After that, you will need to update the ./src/entities/exam.py file as follows:

# coding=utf-8

from marshmallow import Schema, fields

# ... other import statements ...

# ... Exam class definition ...

class ExamSchema(Schema):
    id = fields.Number()
    title = fields.Str()
    description = fields.Str()
    created_at = fields.DateTime()
    updated_at = fields.DateTime()
    last_updated_by = fields.Str()

In the new version of this file, you are using the Schema class of marshmallow to define a new class called ExamSchema. You will use this class to transform instances of Exam into JSON objects.

After defining ExamSchema, you can refactor the ./src/main.py file to expose two endpoints:

# coding=utf-8

from flask import Flask, jsonify, request

from .entities.entity import Session, engine, Base
from .entities.exam import Exam, ExamSchema

# creating the Flask application
app = Flask(__name__)

# if needed, generate database schema
Base.metadata.create_all(engine)


@app.route('/exams')
def get_exams():
    # fetching from the database
    session = Session()
    exam_objects = session.query(Exam).all()

    # transforming into JSON-serializable objects
    schema = ExamSchema(many=True)
    exams = schema.dump(exam_objects)

    # serializing as JSON
    session.close()
    return jsonify(exams.data)


@app.route('/exams', methods=['POST'])
def add_exam():
    # mount exam object
    posted_exam = ExamSchema(only=('title', 'description'))\
        .load(request.get_json())

    exam = Exam(**posted_exam.data, created_by="HTTP post request")

    # persist exam
    session = Session()
    session.add(exam)
    session.commit()

    # return created exam
    new_exam = ExamSchema().dump(exam).data
    session.close()
    return jsonify(new_exam), 201

This file now creates a Flask application, based on SQLAlchemy and PostgreSQL, that is capable of accepting POST requests to create new instances of exam and capable of accepting GET requests to serialize these instances as a JSON array.

Now, to facilitate running this application, you can create a script called bootstrap.sh in the backend directory with the following code:

#!/bin/bash
export FLASK_APP=./src/main.py
source $(pipenv --venv)/bin/activate
flask run -h 0.0.0.0

This script does three things:

  1. it sets ./src/main.py as the value of the FLASK_APP environment variable (this is needed by the last command);
  2. it activates the virtual environment;
  3. and it runs flask listening on all interfaces (-h 0.0.0.0).

Then, to test everything, you can use the following commands:

# make script executable
chmod u+x bootstrap.sh

# execute script in the background
./bootstrap.sh &

# create a new exam
curl -X POST -H 'Content-Type: application/json' -d '{
  "title": "TypeScript Advanced Exam",
  "description": "Tricky questions about TypeScript."
}' http://0.0.0.0:5000/exams

# retrieve exams
curl http://0.0.0.0:5000/exams

The first command on the snippet above transforms the bootstrap.sh script into an executable file. After that, it runs this script in the backend so you can keep using the same terminal. When the Flask application is up and running, you can use the two curl commands to interact with it. The first one issues POST requests to create new exams and the second one lists all exams persisted on the database.

Besides using curl, you can also fetch exams by browsing to http://0.0.0.0:5000/exams.

You have made some good progress. So, it's better to save everything:

git add . && git commit -m "integrating Flask RESTful endpoints and SQLAlchemy"

Handling CORS on Flask Apps

As your Flask app will receive requests from a SPA, you will need to allow CORS on it. If you don't do so, most browsers will block requests to your API because the backend does not explicitly allow Cross-Origin Resource Sharing (CORS).

Luckily, there is a Flask module called flask-cors that is easy to configure. So, to install this module, issue the following command in your backend directory:

pipenv install flask-cors

Then, update the main.py file to take advantage of this module:

# coding=utf-8

from flask_cors import CORS
# ... other import statements ...

# creating the Flask application
app = Flask(__name__)
CORS(app)

# ... create_all(engine) and endpoint definitions ...

Without any further configuration, flask-cors allows CORS for all domains on all routes. During the development process, this configuration will be enough. However, in the future, you will probably want to be more restrictive. When the day comes, check the official documentation of the flask-cors module to learn how to tweak these settings.

Now, before switching to Angular, you can save your progress and leave your Flask application up and running:

# commit your progress
git add . && git commit -m "enabling CORS"

# run the Flask app in the background
./bootstrap.sh &

Bootstrapping the Angular Application

To create your Angular application, you will use the ng tool that Angular CLI made available. So, move back to the project root directory and issue ng new frontend. This will create the basic structure of an Angular app. The following snippet summarizes the commands to create your app and to commit it untouched to your Git repository:

# change working directory to project root
cd ..

# run @angular/cli to bootstrap the Angular app
ng new frontend

# move working directory to your frontend app
cd frontend

# commit template Angular project
git add . && git commit -m "bootstrapping an Angular project"

Consuming Flask Endpoints with Angular

After creating your Angular app, the next thing you will need is to create a file called env.ts inside the ./frontend/src/app directory with the following code:

export const API_URL = 'http://localhost:5000';

For now, this TypeScript module simply exports a single constant (API_URL) that references your Flask backend application running locally. In the third part of this series, you will enhance this module to define different API_URL values depending on the environment.

Now, you can create a new directory called exams inside ./frontend/src/app to hold files related to this entity. In this directory, you will create two files: exam.model.ts and exams-api.service.ts. The first file (exam.model.ts) will have a TypeScript class to represent exams:

export class Exam {
  constructor(
    public title: string,
    public description: string,
    public _id?: number,
    public updatedAt?: Date,
    public createdAt?: Date,
    public lastUpdatedBy?: string,
  ) { }
}

The second file, exams-api.service.ts, will create a service that uses HttpClient to fetch exams from your Flask backend application:

import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import {API_URL} from '../env';
import {Exam} from './exam.model';

@Injectable()
export class ExamsApiService {

  constructor(private http: HttpClient) {
  }

  private static _handleError(err: HttpErrorResponse | any) {
    return Observable.throw(err.message || 'Error: Unable to complete request.');
  }

  // GET list of public, future events
  getExams(): Observable<Exam[]> {
    return this.http
      .get(`${API_URL}/exams`)
      .catch(ExamsApiService._handleError);
  }
}

As this service depends on HttpClient, you will need to import HttpClientModule from Angular in your AppModule declaration. Besides that, you will have to register ExamsApiService as a provider. So, open the ./frontend/src/app/app.module.ts file and replace its contents with this:

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {HttpClientModule} from '@angular/common/http';

import {AppComponent} from './app.component';
import {ExamsApiService} from './exams/exams-api.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
  ],
  providers: [ExamsApiService],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Now, you will have to update the ./frontend/src/app/app.component.ts file to use your new service to fetch data from your Flask app:

import {Component, OnInit, OnDestroy} from '@angular/core';
import {Subscription} from 'rxjs/Subscription';
import {ExamsApiService} from './exams/exams-api.service';
import {Exam} from './exams/exam.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
  title = 'app';
  examsListSubs: Subscription;
  examsList: Exam[];

  constructor(private examsApi: ExamsApiService) {
  }

  ngOnInit() {
    this.examsListSubs = this.examsApi
      .getExams()
      .subscribe(res => {
          this.examsList = res;
        },
        console.error
      );
  }

  ngOnDestroy() {
    this.examsListSubs.unsubscribe();
  }
}

Lastly, you will have to update its template (app.component.html) to show the exams fetched:

<div style="text-align:center">
  <h1>Exams</h1>
</div>
<h2>Here are the exams created so far: </h2>
<ul>
  <li *ngFor="let exam of examsList">
    {{exam.title}}
  </li>
</ul>

With all these changes in place, you can run your Angular application (run ng serve on the frontend directory) to check if everything is working as expected. After Angular finishes compiling your app, you can browse to http://localhost:4200. On this URL, you will see a page similar to this:

This wraps up the first part of the series. Therefore, before moving on to the next parts, don't forget to save your progress:

git add .
git commit -m "integrating Flask and Angular"

"Developing web applications with Angular and Flask is easy!"

Tweet This

Conclusion and Next Steps

In the first part of this series, you used pipenv to bootstrap a Flask backend API. After that, you used SQLAlchemy ORM to integrate your Flask app with a database. Then, you installed and ran Angular CLI to create a new Angular SPA . In the end, you made your SPA fetch exams from the backend to show to visitors. These features together pave the way to create an application that relies on Flask and Angular to deliver a modern user experience.

In the next article, you will learn how modern web apps manage identity and you will enhance both your backend and your frontend apps to include more features. Stay tuned!