Advertisement
🌐 Projects

Python Full-Stack Project – Flask Blog

Build a complete blog web application with Flask: user registration, login, post CRUD, SQLite storage, and Jinja2 templates.

⏱️ 20 min read🎯 Projects📅 Updated 2026

Setup

Bash
pip install flask flask-sqlalchemy flask-login werkzeug

models.py

Python
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash

db = SQLAlchemy()

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(256))
    posts = db.relationship("Post", backref="author", lazy=True)
    def set_password(self, p): self.password_hash = generate_password_hash(p)
    def check_password(self, p): return check_password_hash(self.password_hash, p)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)

app.py

Python
from flask import Flask, render_template, redirect, url_for, request, flash
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from models import db, User, Post

app = Flask(__name__)
app.config["SECRET_KEY"] = "change-in-production"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///blog.db"
db.init_app(app)
login_manager = LoginManager(app)
login_manager.login_view = "login"

@login_manager.user_loader
def load_user(uid): return User.query.get(int(uid))

@app.route("/")
def index():
    return render_template("index.html", posts=Post.query.order_by(Post.created_at.desc()).all())

@app.route("/new", methods=["GET", "POST"])
@login_required
def new_post():
    if request.method == "POST":
        post = Post(title=request.form["title"], content=request.form["content"], author=current_user)
        db.session.add(post); db.session.commit()
        return redirect(url_for("index"))
    return render_template("new_post.html")

@app.route("/register", methods=["GET", "POST"])
def register():
    if request.method == "POST":
        user = User(username=request.form["username"], email=request.form["email"])
        user.set_password(request.form["password"])
        db.session.add(user); db.session.commit()
        login_user(user); return redirect(url_for("index"))
    return render_template("register.html")

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        user = User.query.filter_by(email=request.form["email"]).first()
        if user and user.check_password(request.form["password"]):
            login_user(user); return redirect(url_for("index"))
        flash("Invalid credentials")
    return render_template("login.html")

@app.route("/logout")
@login_required
def logout(): logout_user(); return redirect(url_for("index"))

with app.app_context(): db.create_all()
if __name__ == "__main__": app.run(debug=True)
Bash
python app.py
# http://localhost:5000
Tip: For production: Gunicorn as WSGI server, Nginx as reverse proxy. Never run Flask dev server in production.

🏋️ Practical Exercise

Extend the full-stack app:

  1. Add a new field to the model and update the template to display it.
  2. Add a form to create new records through the web interface.
  3. Add a route to delete a record.
  4. Add basic input validation on the form.

🔥 Challenge Exercise

Grow the app into a complete CRUD web application: add create, edit, and delete flows with forms, server-side validation, and flash messages for feedback. Add simple user authentication so only logged-in users can modify data, and separate concerns into models, routes, and templates. Bonus: deploy it and add a handful of tests for the key routes.

📋 Summary

  • This project builds a full-stack web app with a database, server logic, and HTML templates.
  • Models define the data, routes/views handle requests, and templates render the UI.
  • Forms let users create and edit data, with validation on the server.
  • Data persists in a database via an ORM or direct queries.
  • Authentication restricts who can modify data.
  • Clear separation of concerns keeps the app maintainable and testable.

Interview Questions on Building a Full-Stack App

  • What does “full-stack” mean and what layers are involved?
  • How does a web framework connect models, views, and templates?
  • How do you handle form submissions and validation?
  • How is data persisted in a web app?
  • How do you add user authentication?
  • What is the difference between server-side and client-side rendering?
  • How would you deploy a Python web application?

FAQ

What does “full-stack” actually include? +

It spans the whole application: the front end (HTML/CSS/JS the user sees), the back end (server logic and routing), and the data layer (the database). A full-stack project ties all three together.

How do models, views, and templates fit together? +

Models represent and persist data, views/routes handle incoming requests and decide what to do, and templates turn data into HTML for the browser. The framework wires a URL to a view, which queries models and renders a template.

How do I add login to the app? +

Use the framework’s auth tools (Django’s built-in auth, or Flask extensions like Flask-Login). Store hashed passwords, create login/logout routes and a session, and protect sensitive routes so only authenticated users can reach them.

How would I deploy this? +

Run it behind a production server (Gunicorn/Uvicorn) with a reverse proxy like Nginx, on a platform such as a VPS, Railway, Render, or a container host. Use environment variables for secrets and a managed database in production.