Introduction to Flask in Python

Flask is a lightweight web application framework built on top of Werkzeug and a great choice for developers who want to build or prototype web applications quickly.

In this article, we’ll explore what Flask is, its common uses, and when it might not be the right choice. We’ll also dive into examples, showing you how to set up a Flask project, create a simple application, and organize the code.

What is Flask?

Flask is a web framework based on the Werkzeug WSGI (Web Server Gateway Interface) toolkit using the Jinja2 templating engine. Flask allows developers to handle HTTP requests, create dynamic content, and manage routes.

Unlike some frameworks, Flask provides the essentials and lets you add additional functionality as needed. This gives you the freedom to design your application architecture in a way that suits your specific needs.

What is Flask Used For?

Flask is commonly used for:

  1. Prototyping and MVPs: Its simplicity makes it perfect for building prototypes or minimum viable products.
  2. API Development: Flask’s support for RESTful routing makes it a popular choice for creating APIs.
  3. Small to Medium-Sized Web Applications: For projects that don’t require the heavy lifting of a full-stack framework, Flask is a solid choice.
  4. Custom Web Solutions: Since Flask doesn’t force you into a predefined structure, it’s excellent for custom implementations.

What Should Flask Not Be Used For?

Flask is not ideal for:

  1. Large, Monolithic Applications: While Flask can scale with the help of extensions, managing a massive codebase without enforced structure can become challenging.
  2. Built-In Authentication or Admin Panels: Unlike Django, Flask does not provide these features out of the box. You’d need to implement them yourself or use third-party packages.
  3. Projects Needing Strict Conventions: If your team prefers working with a strict and consistent framework structure, Flask might not be the best fit.

Setting Up a Flask Project

Setting Up the Environment

Before diving into the code, create a virtual environment to keep the project dependencies isolated.

On Windows:

python -m venv venv
venv\Scripts\activate
pip install flask

On Linux:

python3 -m venv venv
source venv/bin/activate
pip install flask

Step 2: Project Structure

Here’s a simple structure for a Flask application:

my_flask_app/

├── app/
│   ├── __init__.py
│   ├── routes.py
│   └── templates/
│       └── index.html

├── venv/

└── run.py

Step 3: Code Example

File: app/__init__.py

This file initializes the Flask application.

from flask import Flask

def create_app():
    app = Flask(__name__)
    
    from .routes import bp
    app.register_blueprint(bp)
    
    return app

File: app/routes.py

Define your application routes in this file.

from flask import Blueprint, render_template, jsonify

bp = Blueprint('main', __name__)

@bp.route('/')
def home():
    return render_template('index.html')

@bp.route('/api/data')
def get_data():
    return jsonify({'message': 'Hello, Flask!', 'status': 'success'})

File: app/templates/index.html

This file serves as a simple template for the home page.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flask App</title>
</head>
<body>
    <h1>Welcome to My Flask App</h1>
    <p>Explore Flask and build something great!</p>
</body>
</html>

File: run.py

The entry point for running the application.

from app import create_app

app = create_app()

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

Step 4: Run the Application

Navigate to the project directory and start the server:

python run.py

Visit http://127.0.0.1:5000 in your web browser to see your Flask application in action.

After confirming your Flask application is running, we can transform it into a text-based survival game powered by JavaScript. In this section, we will create a static folder for your game logic, integrate the game with your existing index.html template, and update your Flask routes to serve everything properly.

Step 1: Create the static Folder

Flask uses a folder named static to serve static files like JavaScript, CSS, and images.

  1. Navigate to your app directory.
  2. Create a folder named static.
  3. Inside the static folder, create a file named game.js and paste the JavaScript code for the survival game into it.

NOTE: JS Code below is a modified version of game code found on codepin.io here. That game was coded by Tracy Spitler.

// Survival Game with Poisonous Cloud and Storyline
class Survival {
    constructor(current, characters, count, treasures) {
      this._current = current; // Current character
      this._characters = characters; // Array of characters
      this._count = count; // Tracks number of moves survived
      this._treasures = treasures; // Treasures collected
      console.log("Welcome to Survival!");
    }
  
// Main Menu
mainMenu() {
    console.log("main menu");
    const input = prompt(
      "Welcome to Survival!\n" +
      "Please select an option from the menu below:\n" +
      "1. Characters (do this first!)\n" +
      "2. Start Game\n" +
      "3. Exit"
    ).toLowerCase();
  
    switch (input) {
      case "1":
      case "characters":
        this.characterMenu();
        break;
  
      case "2":
      case "start game":
        if (this._current) {
          this.startGame();
        } else {
          alert("You must create or select a character before starting the game.");
          this.mainMenu();
        }
        break;
  
      case "3":
      case "exit":
        alert("Exiting...");
        console.log("exit");
        break;
  
      default:
        alert("Invalid input.");
        this.mainMenu();
    }
  }
  
    // Character Menu
    characterMenu() {
      console.log("character menu");
      const input = prompt(
        "What would you like to do?\n" +
        "1. Create a Character\n" +
        "2. Select a Character\n" +
        "3. Main Menu"
      ).toLowerCase();
  
      switch (input) {
        case "1":
        case "create a character":
          this.createCharacter();
          break;
  
        case "2":
        case "select a character":
          this.selectCharacter();
          break;
  
        case "3":
        case "main menu":
          this.mainMenu();
          break;
  
        default:
          alert("Invalid input.");
          this.characterMenu();
      }
    }
  
// Create Character
createCharacter() {
    const name = prompt("What is your survivor's name?");
    if (name && !this._characters.includes(name)) {
      this._characters.push(name);
      this._current = name;
      console.log(`Character created: ${name}`);
      alert(`Your survivor, ${name}, is ready.`);
      this.mainMenu();
    } else {
      alert(`${name} is already a character or invalid.`);
      this.characterMenu();
    }
  }
  
    // Select Character
    selectCharacter() {
      if (this._characters.length > 0) {
        const list = this._characters.join("\n");
        const select = prompt(`Select a character:\n${list}`);
        if (this._characters.includes(select)) {
          this._current = select;
          alert(`${select} is now the current character.`);
          this.mainMenu();
        } else {
          alert("Invalid selection.");
          this.selectCharacter();
        }
      } else {
        alert("No characters exist. Please create one first.");
        this.characterMenu();
      }
    }
  
    // Start Game
    startGame() {
      alert(
        `Welcome, ${this._current}, to Survival!\n` +
        "A deadly cloud is sweeping across the land from the west. It kills anything that breathes oxygen and reanimates their corpses as terrifying ghouls.\n" +
        "You must survive by scavenging for treasures and making wise decisions. Danger is everywhere, and time is running out!"
      );
      this._count = 0;
      this._treasures = 0;
      this.grasslandIntroduction();
    }
  
    // Grassland Introduction
    grasslandIntroduction() {
      alert(
        "You begin your journey on the open grasslands. The vast expanse of green and gold stretches endlessly, but the looming cloud to the west is a constant reminder of the danger.\n" +
        "Despite the beauty of the plains, shadowy figures move within the tall grass, and each step forward brings uncertainty."
      );
      this.makeMove();
    }
  
    // Make a Move
    makeMove() {
      const direction = prompt(
        "Choose a direction to move:\n" +
        "1. North (30/70 risky)\n" +
        "2. South (30/70 risky)\n" +
        "3. East (no risk, but encounters possible)\n" +
        "4. West (60/40 risky, treasures await)"
      ).toLowerCase();
  
      switch (direction) {
        case "1":
        case "north":
          this.riskEncounter(0.3, "north");
          break;
  
        case "2":
        case "south":
          this.riskEncounter(0.3, "south");
          break;
  
        case "3":
        case "east":
          alert("You move east, avoiding the cloud but encountering another survivor. They warn you to tread carefully.");
          this.makeMove();
          break;
  
        case "4":
        case "west":
          alert("You venture west, closer to the poisonous cloud...");
          this.riskEncounter(0.6, "west");
          break;
  
        default:
          alert("Invalid input.");
          this.makeMove();
      }
    }
  
    // Risk Encounter
    riskEncounter(risk, direction) {
      const roll = Math.random();
      console.log(`Risk roll: ${roll}`);
      if (roll < risk) {
        this.triggerZombie(direction);
      } else {
        alert(`You safely moved ${direction}.`);
        if (direction === "west") this._treasures++;
        this._count++;
        this.checkProgress();
      }
    }
  
    // Trigger Zombie Encounter
    triggerZombie(direction) {
      const zombieType = Math.floor(Math.random() * 3) + 1;
      let description;
      switch (zombieType) {
        case 1:
          description = "A decayed figure with glowing eyes and jerky movements stumbles toward you. This is no living creature—it’s a ghoul!";
          break;
        case 2:
          description = "A fast-moving, bloodied zombie with erratic movements lunges at you, its hollow eyes locking onto you with deadly intent.";
          break;
        case 3:
          description = "A hulking zombie, massive and slow, lumbers toward you with blackened veins pulsing and muscles bulging beneath rotted flesh.";
          break;
      }
      alert(`Danger! ${description}\nThe zombie overwhelms you.`);
      this.gameOver();
    }
  
    // Check Progress
    checkProgress() {
      if (this._count >= 10) {
        this.win();
      } else {
        this.makeMove();
      }
    }
  
    // Game Over
    gameOver() {
      alert(
        `Game Over!\n` +
        `Survivor: ${this._current}\n` +
        `Moves survived: ${this._count}\n` +
        `Treasures found: ${this._treasures}`
      );
      this.tryAgain();
    }
  
    // Try Again
    tryAgain() {
      confirm("Would you like to try again?") ? this.startGame() : this.mainMenu();
    }
  
    // Win
    win() {
      alert(
        `Congratulations, ${this._current}!\n` +
        `You survived 10 moves and found ${this._treasures} treasures!`
      );
      this.mainMenu();
    }
  }
  
  const current = null;
  const characters = [];
  const survivalGame = new Survival(current, characters);
  survivalGame.mainMenu();

Your project structure should now look like this:

my_flask_app/
├── app/
│   ├── __init__.py
│   ├── routes.py
│   ├── templates/
│   │   └── index.html
│   └── static/
│       └── game.js
├── venv/
└── run.py

Step 2: Update index.html

Modify your index.html template to load the game.js file and enable the game logic. Replace the previous index.html content with the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Survival Game</title>
</head>
<body>
    <h1>Welcome to the Survival Game</h1>
    <p>Embark on an adventure and see how long you can survive!</p>
    <button id="start-game" onclick="startGame()">Start Game</button>

    <!-- Link the JavaScript file using Flask's url_for function -->
    <script src="{{ url_for('static', filename='game.js') }}"></script>
    <script>
        // Initialize and start the game when the button is clicked
        function startGame() {
            const survivalGame = new Survival(null, [], 0, []);
            survivalGame.mainMenu();
        }
    </script>
</body>
</html>

Step 3: Update routes.py

Ensure your Flask route for the home page renders index.html. The existing routes.py can stay mostly the same, but here’s the full code for reference:

from flask import Blueprint, render_template

bp = Blueprint('main', __name__)

@bp.route('/')
def home():
    return render_template('index.html')

Step 4: Start the Server and Test

Start your Flask server with:

python run.py

Open your web browser and visit: http://127.0.0.1:5000

  • You should now see the survival game interface with a button labeled “Start Game.” When clicked, the JavaScript survival game logic will load and run directly in your browser.

What You’ve Built

You’ve integrated a dynamic text-based survival game with Flask, showcasing how the framework can serve static files and render templates seamlessly. This project demonstrates how Flask can be used as the backend for serving a frontend-heavy application while giving you complete flexibility to integrate interactive elements like games.

A Survival Game in Flask/Python


Thank you for reading this article. I hope you found it helpful and informative. If you have any questions, or if you would like to suggest new Python code examples or topics for future tutorials, please feel free to reach out. Your feedback and suggestions are always welcome!

Happy coding!
C. C. Python Programming

You can also find this at Medium.com

Leave a Reply