Skip to main content
Flask Basics Tutorial
CHAPTER 17 Beginner

Building a Complete Flask Project

Updated: May 14, 2026
45 min read

# CHAPTER 17

Building a Complete Flask Project

1. Introduction

You have studied Routing, Jinja2, SQLAlchemy, WTForms, and Security. Theoretical knowledge is essential, but hiring managers want to see practical execution. In this chapter, we will synthesize all your knowledge to architect a real-world, modular portfolio project: a Developer Job Board platform. This project mimics the exact technical requirements you will encounter in a professional backend engineering role.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Architect a modular Flask project using the Application Factory pattern.
  • Define relational database schemas (One-to-Many).
  • Implement secure authentication workflows.
  • Protect data mutation routes using session validation.

3. Project Overview: The Job Board

Requirements:
  • The project will consist of two Blueprints: authbp (Login/Register) and jobsbp (Core Logic).
  • Employers must be able to register and log in.
  • Authenticated Employers can Create, Update, and Delete job postings.
  • Unauthenticated Job Seekers can Read all postings on the public homepage.
  • Crucial Security Rule: Employer A cannot edit or delete Employer B's job posts.

4. Step 1: The Architecture

Create a new project structure:
text
12345678910
job_board/
    run.py
    core/
        __init__.py
        models.py
        forms.py
        auth/
            routes.py
        jobs/
            routes.py

5. Step 2: The Database Schemas

Open core/models.py. We link the Job to a specific User using a Foreign Key relationship.
python
12345678910111213141516171819
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

db = SQLAlchemy()

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)
    # The relationship (One user has many jobs)
    jobs = db.relationship('Job', backref='employer', lazy=True)

class Job(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    description = db.Column(db.Text, nullable=False)
    # The Foreign Key
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    date_posted = db.Column(db.DateTime, default=datetime.utcnow)

6. Step 3: The Application Factory

Open core/_init_.py. We assemble the pieces securely.
python
123456789101112131415161718192021
from flask import Flask
from .models import db

def create_app():
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'super-secret-key'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///jobboard.db'
    
    db.init_app(app)
    
    # Register Blueprints
    from .auth.routes import auth_bp
    from .jobs.routes import jobs_bp
    app.register_blueprint(auth_bp)
    app.register_blueprint(jobs_bp)
    
    # Create the database tables
    with app.app_context():
        db.create_all()
        
    return app

7. Step 4: The Core Logic (Jobs Blueprint)

Open core/jobs/routes.py. Here we enforce our strict security rules.
python
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
from flask import Blueprint, render_template, request, redirect, url_for, session, abort
from core.models import db, Job
from core.forms import JobForm # Assume you built a WTForm for this

jobs_bp = Blueprint('jobs', __name__)

# 1. READ ALL (Public)
@jobs_bp.route('/')
def home():
    # Fetch all jobs, ordered by newest
    all_jobs = Job.query.order_by(Job.date_posted.desc()).all()
    return render_template('home.html', jobs=all_jobs)

# 2. CREATE (Secure)
@jobs_bp.route('/job/new', methods=['GET', 'POST'])
def create_job():
    # Security Check: Must be logged in!
    if 'user_id' not in session:
        return redirect(url_for('auth.login'))
        
    form = JobForm()
    if form.validate_on_submit():
        # Securely bind the job to the currently logged-in user
        new_job = Job(
            title=form.title.data, 
            description=form.description.data, 
            user_id=session['user_id']
        )
        db.session.add(new_job)
        db.session.commit()
        return redirect(url_for('jobs.home'))
        
    return render_template('job_form.html', form=form)

# 3. DELETE (Highly Secure)
@jobs_bp.route(&#039;/job/<int:id>/delete', methods=['POST'])
def delete_job(id):
    if &#039;user_id' not in session:
        abort(403) # Forbidden
        
    job_to_delete = Job.query.get_or_404(id)
    
    # CRITICAL SECURITY CHECK: Does the logged-in user own this specific job?
    if job_to_delete.user_id != session[&#039;user_id']:
        abort(403) # Forbidden: You can't delete someone else's job!
        
    db.session.delete(job_to_delete)
    db.session.commit()
    return redirect(url_for(&#039;jobs.home'))

8. Reviewing the Architecture

Look closely at the deletejob View function. We didn't just check if a user was logged in. We fetched the requested Job object from the database and explicitly checked if jobtodelete.userid != session['user_id']. Without this line, a malicious logged-in user could run a script hitting /job/1/delete, /job/2/delete, and wipe out every job board posting in your entire database. Verifying ownership is the hallmark of professional backend security.

9. Step 5: Running the Project

Open run.py at the root of your project:
python
123456
from core import create_app

app = create_app()

if __name__ == &#039;__main__':
    app.run(debug=True)

Run python run.py. You have just orchestrated a professional, enterprise-grade web application!

10. Summary

You just built a multi-user SaaS backend!
  1. 1. Blueprints modularized the routing structure.
  1. 2. Models established relational constraints (One-to-Many) between Employers and Jobs.
  1. 3. WTForms automated input generation and sanitized the data.
  1. 4. Session Logic protected the routes and identified the user.
  1. 5. Views handled database queries and enforced strict ownership authorization.

You now have a robust, portfolio-ready project demonstrating your mastery of the Flask framework.

11. Next Chapter Recommendation

Before deploying this masterpiece, how do we prove the code actually works without manually clicking every button on the website? Proceed to Chapter 18: Testing and Debugging Flask Applications.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·