A lightweight, custom-built forum application using pure Python (no external dependencies) with an MVC architecture, custom template engine, and SQLite database.
# Start the server
python main.py
# Open in browser
http://127.0.0.1:8000- Navigate to:
http://127.0.0.1:8000/signup - Enter username and password
- Click Sign Up
- Automatically logged in
- Navigate to:
http://127.0.0.1:8000/login - Enter credentials
- Session stored in memory
Want to make a user an admin? Follow one of these two easy methods.
Important: stop the server before editing the database to avoid in-memory state mismatch; restart the server after changes.
Method A — Quick (SQLite CLI)
- Stop the server (Ctrl+C).
- Open a terminal in the project folder and run:
sqlite3 forum.db- Inside sqlite prompt run:
SELECT id, username, is_admin FROM users WHERE username = 'YOUR_USERNAME';
UPDATE users SET is_admin = 1 WHERE username = 'YOUR_USERNAME';- Exit sqlite:
.exit- Restart the server and log in as that user, then visit
http://127.0.0.1:8000/adminto verify.
Method B — Safe (one-line Python, recommended)
- Stop the server.
- Run this from the project root (PowerShell / bash):
python - <<'PY'
from app.repository.base_repo import get_conn
u = 'YOUR_USERNAME'
conn = get_conn()
cur = conn.cursor()
cur.execute('SELECT id, username, is_admin FROM users WHERE username = ?', (u,))
print('Before ->', cur.fetchone())
cur.execute('UPDATE users SET is_admin = 1 WHERE username = ?', (u,))
conn.commit()
cur.execute('SELECT id, username, is_admin FROM users WHERE username = ?', (u,))
print('After ->', cur.fetchone())
conn.close()
PY- Restart the server and check
http://127.0.0.1:8000/admin.
Revoke admin access
sqlite3 forum.db
UPDATE users SET is_admin = 0 WHERE username = 'YOUR_USERNAME';
.exitQuick troubleshooting
- If the SELECT returns no rows, create the user first via
/signup. - If the admin page still doesn't load, restart the server and clear browser cookies.
That's it — two safe, simple options to set (or remove) admin status.
✅ User System
- Registration with password hashing (SHA256)
- Session-based authentication
- CSRF token protection
- Role-based access (User, Admin)
✅ Posts & Comments
- Create, read, update, delete posts
- Comment on posts
- Vote on posts and comments
✅ Social Features
- Follow/unfollow users
- Personalized feed
- Reputation system
- Badge awarding
- Notifications
✅ Security
- Password hashing with random salts
- CSRF tokens on all forms
- Rate limiting (60 req/min default)
- Authorization checks (401, 403)
✅ Zero Dependencies
- Pure Python standard library
- sqlite3 for database
- wsgiref for WSGI server
- No external packages needed
| Component | Technology |
|---|---|
| Language | Python 3.10+ |
| Server | wsgiref.simple_server (WSGI) |
| Database | SQLite |
| Template Engine | Custom Python parser/renderer |
| Security | SHA256 hashing, CSRF tokens, rate limiting |
Python-MVC-Forum/
├── main.py # Server entry point
├── config.py # Configuration
├── forum.db # SQLite database
├── README.md # Documentation
├── LICENSE # Proprietary license
│
├── core/ # Framework core
│ ├── router.py # URL routing
│ ├── dispatcher.py # WSGI application
│ ├── request.py # Request parsing
│ ├── response.py # Response building
│ ├── session.py # Session management
│ └── security.py # Hashing, CSRF, rate limiting
│
├── app/
│ ├── models/ # Data models
│ ├── repository/ # Data access (CRUD)
│ ├── services/ # Business logic
│ ├── controllers/ # Route handlers
│ ├── templates/ # HTML templates
│ └── templates_engine/ # Custom template engine
│
├── static/css/style.css # Stylesheet
├── tests/ # Unit tests
└── requirements.txt # Dependencies (empty)
- Create:
/posts/create(logged in only) - View:
/posts(all posts) or/posts/{id}(single post) - Vote: Upvote/downvote to increase/decrease author reputation
- Delete: Post author only
- Add comments to posts
- Vote on comments
- Voting updates author's reputation
- Upvote (+1): Increases author reputation
- Downvote (-1): Decreases reputation
- One vote per user per post/comment
Auto-awarded badges:
- Newcomer: First post
- Popular: 10+ votes on a post
- Expert: 100+ reputation points
- Moderator: Admin-awarded
View Badges: User profile page
Sent for:
- Comments on your posts
- Votes on your posts
- Follows from other users
View: Navigate to /notifications when logged in
- Follow users to see their posts in your feed
- Unfollow anytime
- Generates notifications
| Method | Endpoint | Description |
|---|---|---|
| GET | / |
Home (all posts) |
| GET | /posts |
Posts list |
| GET | /posts/{id} |
Single post view |
| GET | /users/{username} |
User profile |
| GET | /signup |
Signup form |
| POST | /signup |
Register user |
| GET | /login |
Login form |
| POST | /login |
Authenticate |
| Method | Endpoint | Description |
|---|---|---|
| GET | /posts/create |
Create form |
| POST | /posts/create |
Submit post |
| POST | /posts/{id}/vote |
Vote on post |
| POST | /posts/{id}/comment |
Add comment |
| GET | /feed |
User feed |
| GET | /notifications |
Notifications |
| GET | /logout |
Logout |
| Method | Endpoint | Required |
|---|---|---|
| GET | /admin |
is_admin=1 |
Algorithm: SHA256 with random salt
Format: salt$hash
Stored: sha256(salt + password)
- Token generated on form GET
- Token stored in session
- Form includes hidden csrf input
- Token validated on POST
- Invalid token returns 400
- Stored in-memory (expires on restart)
- HTTP-Only cookies
- 24-hour max age
- Contains: user, csrf token
- 60 requests per IP per minute (default)
- Configured in
config.py - Sliding window per IP address
Variables:
<h1>{{ title }}</h1>
<p>Author: {{ post.author }}</p>Conditionals:
{% if user %}
<p>Welcome, {{ user }}!</p>
{% else %}
<p><a href="/login">Please sign in</a></p>
{% endif %}Loops:
<ul>
{% for post in posts %}
<li>{{ post.title }} by {{ post.author }}</li>
{% endfor %}
</ul>- Parser (
app/templates_engine/parser.py): Tokenizes and builds AST - Renderer (
app/templates_engine/renderer.py): Evaluates with context
id | username | password_hash | reputation | is_admin | badges | created_atid | title | content | author | votes | created_at | updated_atid | post_id | author | content | votes | created_atid | voter | voteable_id | voteable_type | value | created_atid | follower | following | created_atid | user_id | message | is_read | created_atid | name | criteria | created_atpython -m unittest discover tests -vtests/test_router.py: URL routing and parameterstests/test_template_engine.py: Variable/for/if renderingtests/test_repo.py: User/post CRUD operationstests/test_auth.py: Registration and authentication
Ran 7 tests in 0.XXXs
OK
Edit config.py:
HOST = '127.0.0.1' # Server address
PORT = 8000 # Server port
DB_PATH = 'forum.db' # Database file
RATE_LIMIT_PER_MINUTE = 60 # Rate limit per IPCommon Changes:
Network access:
HOST = '0.0.0.0' # Instead of 127.0.0.1Different port:
PORT = 8001Different database:
DB_PATH = '/var/lib/forum/forum.db'Solution:
- Change
PORTinconfig.py - Or kill process:
taskkill /PID <PID> /F(Windows)
Error: Invalid CSRF token
Solution:
- Clear browser cookies
- Restart server (resets sessions)
- Ensure cookies are enabled
Error: Unknown tag else
Solution:
- Check syntax:
{% endif %}closes{% if %} - Verify variable names:
{{ post.title }}not{{ post['title'] }} - Use valid Python expressions
Error: database is locked
Solution:
- Close SQLite CLI if open
- Restart server
netstat -ano | findstr :8000
taskkill /PID <PID> /FHTTP Request
↓
Dispatcher (core/dispatcher.py)
↓
Router (core/router.py) - URL matching
↓
Controller (app/controllers/*) - Handler
↓
Service (app/services/*) - Business logic
↓
Repository (app/repository/*) - Data access
↓
Database (forum.db)
↓
Template (app/templates/*) - Render HTML
↓
Response (core/response.py)
↓
HTTP Response (HTML)
- Models: Data structures
- Views: HTML templates
- Controllers: Route handlers
- Repository: Data layer
- Services: Business logic
- Python 3.10+
- Windows, macOS, or Linux
- No external dependencies (pure stdlib)
python main.py- Use production WSGI server (Gunicorn, uWSGI)
- Set
HOST = '0.0.0.0'for network access - Use environment variables for config
- Enable HTTPS/SSL
- Use persistent session storage
- Add database backups
- Python 3.10+ installed
- Repository cloned
- Run:
python main.py - Open:
http://127.0.0.1:8000 - Sign up for account
- Create a post
- Vote on posts
- Follow users
- View notifications
- For admin access, see Admin Dashboard
PROPRIETARY LICENSE - ALL RIGHTS RESERVED
© 2025 Arian (Amsh23)
- Use personally
- Modify for personal use
- Copy the code
- Distribute the code
- Use commercially without permission
- Reverse engineer
- Sell the software
- Create derivatives
This software is provided "as is" without warranty. Author is not responsible for damages or data loss.
Contact the author for commercial licensing options.
- Check Troubleshooting section
- Review test files for usage examples
- Check inline code comments
- Report issues via GitHub
- Language: Python
- Framework: Custom MVC
- Database: SQLite
- Lines of Code: ~2000+
- Test Coverage: 7 unit tests
- Version: 1.0.0
- Status: Production Ready
- Persistent session storage
- Email notifications
- User messaging
- Advanced search
- Tagging system
- Content moderation tools
- REST API
- Frontend JavaScript
- Docker support
- Deployment guides
Last Updated: November 30, 2025 Author: Amir (Amsh23) Repository: https://github.com/Amsh23/Python-MVC-Forum