Introduction to Flask for Python

Jul 10, 2024

Introduction to Flask for Python

Overview

  • Instructor: Jake Krieger
  • Goal: Build a basic CRUD application using Flask
  • Application: TaskMaster (a to-do list)
  • Assumes prior knowledge of Python and HTML/CSS
  • Final deployment: Heroku

Setting Up Environment

Prerequisites

  • Python 3.6 or newer
  • Virtual environment (virtualenv package)
  • Text editor (recommended: Visual Studio Code)
  • Flask and Flask-SQLAlchemy

Steps

  1. Install Python

    • Check current version: python --version
    • Use Python 3 for the project: python3
  2. Set Up Virtual Environment

    • Install: pip3 install virtualenv
    • Create env: virtualenv venv
    • Activate: source venv/bin/activate
  3. Install Flask Packages

    • Flask: pip3 install Flask
    • Flask-SQLAlchemy: pip3 install Flask-SQLAlchemy

Creating Basic Flask App

Python Code

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return "Hello, world!"

if __name__ == '__main__':
    app.run(debug=True)

Running the App

  • Execute: python3 app.py
  • Open browser: localhost:5000

Enhancing the App

Template and Static Content

  • Templates folder: templates
  • Static folder: static

Basic Template Setup

  • Create index.html in templates
  • Update Python code to use template:
    from flask import render_template
    @app.route('/')
    def index():
        return render_template('index.html')
    
  • Template inheritance with Jinja2
    • Create base.html
    • Extend in index.html

Using CSS

  • Create static/css/main.css
  • Link in base.html using Flask's url_for
    <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
    

Adding Database Functionality

Setup SQLAlchemy

from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)

Creating Models

class ToDo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.String(200), nullable=False)
    date_created = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return f'<Task {self.id}>'

Initialize and Create Database

  • Interactive shell: python3
    • Commands:
      from app import db
      db.create_all()
      

Building CRUD Functionality

Adding Tasks (Create)

  • HTML Form in index.html
    <form action="{{ url_for('index') }}" method="POST">
        <input type="text" name="content" id="content">
        <input type="submit" value="Add Task">
    </form>
    
  • Flask route for POST requests
    from flask import request, redirect
    @app.route('/', methods=['POST', 'GET'])
    def index():
        if request.method == 'POST':
            task_content = request.form['content']
            new_task = ToDo(content=task_content)
    
            try:
                db.session.add(new_task)
                db.session.commit()
                return redirect('/')
            except:
                return 'There was an issue adding your task'
        else:
            tasks = ToDo.query.order_by(ToDo.date_created).all()
            return render_template('index.html', tasks=tasks)
    

Displaying Tasks (Read)

  • Update index.html with Jinja2 for loop
    {% for task in tasks %}
        <tr>
            <td>{{ task.content }}</td>
            <td>{{ task.date_created.date() }}</td>
            <td>
                <a href="{{ url_for('delete', id=task.id) }}">Delete</a>
                <a href="{{ url_for('update', id=task.id) }}">Update</a>
            </td>
        </tr>
    {% endfor %}
    

Deleting Tasks (Delete)

  • Flask route to delete tasks
    @app.route('/delete/<int:id>')
    def delete(id):
        task_to_delete = ToDo.query.get_or_404(id)
    
        try:
            db.session.delete(task_to_delete)
            db.session.commit()
            return redirect('/')
        except:
            return 'There was a problem deleting that task'
    

Updating Tasks (Update)

  • HTML template update.html
    <form action="{{ url_for('update', id=task.id) }}" method="POST">
        <input type="text" name="content" id="content" value="{{ task.content }}">
        <input type="submit" value="Update">
    </form>
    
  • Flask route to update tasks
    @app.route('/update/<int:id>', methods=['POST', 'GET'])
    def update(id):
        task = ToDo.query.get_or_404(id)
    
        if request.method == 'POST':
            task.content = request.form['content']
    
            try:
                db.session.commit()
                return redirect('/')
            except:
                return 'There was an issue updating your task'
        else:
            return render_template('update.html', task=task)
    

Handling Empty Tasks List

  • Update index.html to show message when no tasks
    {% if tasks|length < 1 %}
        <h2>There are no tasks, create one below:</h2>
    {% else %}
        <!-- Display tasks table -->
    {% endif %}
    

Deploying to Heroku

Create Heroku Account and CLI Setup

  • Sign up at Heroku.com
  • Install Heroku CLI
  • Log in with CLI: heroku login

Prepare App for Deployment

  • Install Gunicorn: pip3 install gunicorn
  • Freeze requirements: pip3 freeze > requirements.txt
  • Create Procfile
    web: gunicorn app:app
    
  • Initialize Git repository
    git init
    

git add . git commit -m "Initial commit"


### Deploy to Heroku

- Create Heroku app: `heroku create <app-name>`
- Push to Heroku: `git push heroku master`
- Open app: `heroku open`

### Verify Deployment

- Ensure CRUD functionalities work on Heroku-hosted app

## Conclusion

- GitHub repository available for reference
- Basic Flask CRUD app created and deployed with detailed steps