Django learning is done, DevBoard is still being finished in parallel, and today, for Day 78, Flask starts. Flask has a reputation for being the opposite of Django in almost every way; where Django gives you everything, Flask gives you almost nothing and lets you choose. Today I set it up, understood what makes it different, and got a first app running.
Flask vs Django — The Key Difference
Before writing a single line of Flask, it's worth understanding the philosophy difference because it changes how you think about everything.
Django is batteries included. You get an ORM, an admin panel, authentication, form handling, and a templating engine, all built in and all opinionated about how you should use them. You work within Django's structure.
Flask is a microframework. You get routing, a templating engine, and a development server. That's essentially it. No ORM, no admin panel, no built-in auth. You pick the tools you want and wire them together yourself.
Neither is better. They're built for different mindsets. Django is faster to get a standard app running. Flask gives you more control and is easier to keep lightweight.
Setup
mkdir flask-app
cd flask-app
python -m venv env
source env/bin/activate
pip install flask
That's the entire installation. Compare that to Django, where you also install djangorestframework, dj-database-url, python-decouple, Pillow before even starting. Flask starts with one package.
The Simplest Flask App
Create app.py:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Hello from Flask!'
if __name__ == '__main__':
app.run(debug=True)
Run it:
python app.py
Visit http://127.0.0.1:5000/ — you see "Hello from Flask!"
That's a complete web application in 8 lines. No project folder, no settings.py, no manage.py, no apps to register. Just a file.
Breaking It Down
app = Flask(__name__)
This creates the Flask application instance. __name__ tells Flask where to look for templates and static files; it uses the location of the current file as the reference point.
@app.route('/')
def home():
return 'Hello from Flask!'
The @app.route('/') decorator registers the function as the handler for the / URL. The function returns a string, which Flask sends as the HTTP response. In Django, you'd write a view function and separately register it in urls.py. In Flask, the route and the view are defined together in one place.
if __name__ == '__main__':
app.run(debug=True)
debug=True enables the debugger and auto-reloads the server when you change the code. Never use debug=True in production.
Multiple Routes
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Home Page'
@app.route('/about')
def about():
return 'About Page'
@app.route('/contact')
def contact():
return 'Contact Page'
if __name__ == '__main__':
app.run(debug=True)
Each route is just a decorator above a function. As clean as it gets.
URL Variables
@app.route('/user/<username>')
def user_profile(username):
return f'Profile of {username}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'Post number {post_id}'
<username> captures a string. <int:post_id> captures an integer. Same concept as Django's <str:slug> and <int:pk> URL patterns, just different syntax and defined right on the route instead of in a separate urls.py.
The flask CLI
Flask also has a command line interface: an alternative to running python app.py directly:
export FLASK_APP=app.py
export FLASK_DEBUG=1
flask run
Or with the newer approach using environment variables in a .flaskenv file:
pip install python-dotenv
Create .flaskenv:
FLASK_APP=app.py
FLASK_DEBUG=1
Now just run flask run and Flask reads the configuration automatically. This is Flask's equivalent of Django's manage.py runserver.
Returning HTML
So far, we've been returning plain strings. Flask can return HTML directly:
@app.route('/')
def home():
return '<h1>Hello from Flask</h1><p>This is a paragraph.</p>'
This works, but gets ugly fast. Templates solve this. For now, this shows that Flask's return is just an HTTP response, and you control exactly what goes in it.
HTTP Methods
By default, a route only handles GET requests. To handle POST:
@app.route('/submit', methods=['GET', 'POST'])
def submit():
if request.method == 'POST':
return 'Form submitted'
return 'Show the form'
In Django, we check if request.method == 'POST': inside the view. Flask works the same way, just with the method restriction declared on the route decorator.
The Response Object
Flask lets you control the response in detail:
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/')
def home():
response = make_response('Hello from Flask', 200)
response.headers['X-Custom-Header'] = 'value'
return response
Most of the time, you just return a string or a rendered template, and Flask handles the response automatically. make_response is there when you need explicit control over status codes or headers.
JSON Responses
Flask has a built-in jsonify function for returning JSON:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/status')
def status():
return jsonify({
'status': 'ok',
'message': 'Flask is running',
'version': '1.0'
})
Visit /api/status and you get a properly formatted JSON response with the correct Content-Type header. No DRF needed for simple JSON endpoints; Flask handles it natively.
Flask vs Django Side by Side
Already, some patterns are clear:
| Django | Flask |
|---|---|
urls.py + view function |
@app.route() decorator on the function |
python manage.py runserver |
flask run or python app.py
|
HttpResponse |
return string or make_response()
|
JsonResponse |
jsonify() |
<int:pk> in URL |
<int:pk> in route — same syntax |
Separate settings.py
|
app.config dictionary |
The concepts are identical. The syntax and where things live are different.
Wrapping Up
First day of Flask and already the contrast with Django is clear. Flask is genuinely lighter: one file, one import, running in seconds. The tradeoff is that everything Django gave you for free: database, auth, admin, you'll have to add piece by piece. That's what the next few days are for.
Tomorrow: routes deeper, URL building, request object, and handling different HTTP methods properly.
Thanks for reading. Feel free to share your thoughts!
Top comments (0)