📝 Portfolio Tracker (SQLite) — Architecture¶
A Flask web application built following the official Flask tutorial, demonstrating core Flask design patterns: application factory, blueprints, request context, and session management.
Technology Stack¶
| Layer | Technology | Purpose |
|---|---|---|
| Language | Python 3.13+ | Application runtime |
| Web Framework | Flask 3.1.2 | Routes, templates, request handling |
| Database | SQLite3 (built-in) | Lightweight relational database |
| Templates | Jinja2 | Server-side HTML rendering |
| Password Hashing | Werkzeug | PBKDF2-based password security |
| Timezone | pytz | UTC → Asia/Singapore conversion |
| CLI | Click (Flask dependency) | init-db command |
| Testing | pytest + coverage | Unit tests and code coverage |
| Build System | Flit | Modern Python packaging |
Application Structure¶
app_portfolio_tracker/
├── __init__.py # Application factory (create_app)
├── auth.py # Authentication blueprint
├── blog.py # Blog/content blueprint
├── db.py # Database utilities
├── logic.py # Business logic (extensible)
├── schema.sql # Database schema
├── static/
│ └── style.css
└── templates/
├── base.html # Base template with navigation
├── auth/
│ ├── login.html
│ └── register.html
└── blog/
├── index.html
├── create.html
└── update.html
Architecture Breakdown¶
1. Application Factory Pattern¶
The entry point is create_app() in __init__.py — the Flask application factory:
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'portfolio_tracker.sqlite'),
)
# Blueprint registration, database init, URL routing
return app
Why this pattern?
- Enables multiple app instances (testing vs. production)
- Allows dynamic configuration injection
- Prevents circular imports
- Standard Flask best practice
2. Blueprint Architecture¶
The application is split into two blueprints for modular organisation:
| Blueprint | URL Prefix | Responsibility |
|---|---|---|
auth |
/auth |
Registration, login, logout, access control |
blog |
/ (root) |
Post CRUD, listing, author verification |
3. Database Layer¶
| Function | Purpose |
|---|---|
get_db() |
Returns request-scoped SQLite connection via Flask's g object |
close_db() |
Teardown handler — closes connection at end of request |
init_db() |
Executes schema.sql to create tables |
init_db_command() |
CLI: flask init-db |
Schema:
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE post (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
title TEXT NOT NULL,
body TEXT NOT NULL,
FOREIGN KEY (author_id) REFERENCES user (id)
);
4. Authentication Flow¶
- Registration → Validate input → Hash password (PBKDF2) → Insert user
- Login → Verify credentials → Store
user_idin signed session cookie - Per-request →
load_logged_in_user()runs before every request, loadsg.userfrom session - Access control →
@login_requireddecorator redirects unauthenticated users
5. Request Lifecycle¶
Example: User creates a new blog post.
- Browser sends
GET /create load_logged_in_user()fires → loads user from session intog.user@login_requiredchecksg.user→ redirects to login if missingcreate()renders the form template- User submits
POST /createwith form data - View validates input → inserts post with
g.user['id']as author → commits close_db()teardown closes the SQLite connection- Redirect to index
Key Design Patterns¶
| Pattern | Implementation |
|---|---|
| Application Factory | create_app() creates configured Flask instance |
| Blueprint | Modular route organisation (auth, blog) |
| Request Context | g.user, g.db — per-request data, auto-cleaned |
| Session Management | Signed cookies, session['user_id'] |
| Decorator | @login_required, @bp.before_app_request |
| Template Inheritance | base.html → child templates extend blocks |
Screenshots¶
Coming soon
Screenshots of the blog interface will be added here.