Flask Basics: Your First Web Application
Welcome to Flask Basics! Think of Flask as a personal chef in a restaurant - it takes your ingredients (Python code) and serves them up as delicious web pages to hungry visitors (users).
What is Flask?
Flask is a micro web framework for Python. โMicroโ doesnโt mean itโs weak - it means itโs focused and extensible. Flask gives you the essentials to build web applications, then lets you add what you need.
Installing Flask
First, letโs set up our development environment:
# Create a virtual environment
python -m venv venv
# Activate it (Windows)
venv\Scripts\activate
# Activate it (macOS/Linux)
# source venv/bin/activate
# Install Flask
pip install flask
# Verify installation
python -c "import flask; print(flask.__version__)"
Your First Flask Application
Letโs create the simplest possible Flask app:
# app.py
from flask import Flask
# Create Flask application instance
app = Flask(__name__)
# Define a route
@app.route('/')
def home():
return "Hello, World!"
# Run the application
if __name__ == '__main__':
app.run(debug=True)
What happens here:
Flask(__name__)creates your app instance@app.route('/')tells Flask what URL to respond tohome()is the function that runs when someone visits/app.run(debug=True)starts the development server
Running Your App
# Run the application
python app.py
# You should see:
# * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
# * Restarting with stat
# * Debugger is active!
# * Debugger PIN: 123-456-789
Open your browser and go to http://127.0.0.1:5000/ - you should see โHello, World!โ
Understanding Routes
Routes connect URLs to Python functions. Letโs add more routes:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Welcome to my website!"
@app.route('/about')
def about():
return "This is the about page."
@app.route('/contact')
def contact():
return "Get in touch with us!"
if __name__ == '__main__':
app.run(debug=True)
Now visit:
http://127.0.0.1:5000/โ โWelcome to my website!โhttp://127.0.0.1:5000/aboutโ โThis is the about page.โhttp://127.0.0.1:5000/contactโ โGet in touch with us!โ
Dynamic Routes
Routes can include variables:
from flask import Flask
app = Flask(__name__)
@app.route('/user/<name>')
def greet_user(name):
return f"Hello, {name}!"
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f"Showing post number {post_id}"
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
return f"Subpath: {subpath}"
if __name__ == '__main__':
app.run(debug=True)
Route converters:
int- integers onlyfloat- floating point numbersstring- text (default)path- text including slashesuuid- UUID strings
HTTP Methods
Web browsers use different HTTP methods. Flask can handle them:
from flask import Flask, request
app = Flask(__name__)
@app.route('/method', methods=['GET', 'POST', 'PUT', 'DELETE'])
def handle_method():
return f"Method used: {request.method}"
# Shorthand for GET (default)
@app.route('/get-only')
def get_only():
return "This only accepts GET requests"
# Multiple methods
@app.route('/post-or-put', methods=['POST', 'PUT'])
def post_or_put():
return f"Received {request.method} request"
if __name__ == '__main__':
app.run(debug=True)
Returning Different Content Types
Flask can return HTML, JSON, files, and more:
from flask import Flask, jsonify, make_response
app = Flask(__name__)
@app.route('/html')
def html_response():
return "<h1>This is HTML!</h1>"
@app.route('/json')
def json_response():
data = {
"message": "Hello, World!",
"status": "success",
"data": [1, 2, 3]
}
return jsonify(data)
@app.route('/custom')
def custom_response():
response = make_response("Custom response")
response.headers['Content-Type'] = 'text/plain'
response.status_code = 200
return response
@app.route('/redirect')
def redirect_example():
from flask import redirect, url_for
return redirect(url_for('home'))
if __name__ == '__main__':
app.run(debug=True)
Application Configuration
Configure your Flask app for different environments:
from flask import Flask
app = Flask(__name__)
# Basic configuration
app.config['SECRET_KEY'] = 'your-secret-key-here'
app.config['DEBUG'] = True
app.config['TESTING'] = False
# Or load from environment
import os
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key')
app.config['DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///app.db')
# Configuration classes
class Config:
SECRET_KEY = 'dev-secret'
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
SECRET_KEY = os.environ.get('SECRET_KEY')
# Use configuration
app.config.from_object(Config())
@app.route('/')
def home():
debug_mode = "ON" if app.config['DEBUG'] else "OFF"
return f"Debug mode: {debug_mode}"
if __name__ == '__main__':
app.run()
Flask Application Factory Pattern
For larger applications, use the application factory pattern:
# app.py
from flask import Flask
def create_app(config_name='development'):
app = Flask(__name__)
# Configuration
if config_name == 'development':
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'dev-secret'
elif config_name == 'production':
app.config['DEBUG'] = False
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
# Register routes
register_routes(app)
return app
def register_routes(app):
@app.route('/')
def home():
return "Hello from factory app!"
# run.py
from app import create_app
app = create_app('development')
if __name__ == '__main__':
app.run()
Error Handling
Handle errors gracefully:
from flask import Flask, render_template
app = Flask(__name__)
@app.errorhandler(404)
def page_not_found(error):
return "Page not found - 404", 404
@app.errorhandler(500)
def internal_error(error):
return "Internal server error - 500", 500
# Custom error pages
@app.errorhandler(404)
def not_found(error):
return """
<h1>404 - Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<a href="/">Go Home</a>
""", 404
# Error handling in routes
@app.route('/divide/<int:a>/<int:b>')
def divide(a, b):
try:
result = a / b
return f"{a} รท {b} = {result}"
except ZeroDivisionError:
return "Cannot divide by zero!", 400
if __name__ == '__main__':
app.run(debug=True)
Logging
Add logging to your Flask app:
import logging
from flask import Flask
app = Flask(__name__)
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@app.route('/')
def home():
logger.info("Home page accessed")
return "Check the console for logs!"
@app.route('/error')
def error_demo():
try:
1 / 0
except Exception as e:
logger.error(f"Error occurred: {e}")
return "An error occurred - check logs", 500
if __name__ == '__main__':
app.run(debug=True)
Practical Examples
Example 1: Simple Blog
from flask import Flask
from datetime import datetime
app = Flask(__name__)
# Mock blog posts (in real app, this would be a database)
posts = [
{
'id': 1,
'title': 'First Post',
'content': 'This is my first blog post!',
'date': '2023-01-01'
},
{
'id': 2,
'title': 'Second Post',
'content': 'This is my second blog post!',
'date': '2023-01-02'
}
]
@app.route('/')
def home():
return "Welcome to my blog!"
@app.route('/posts')
def list_posts():
html = "<h1>My Blog Posts</h1><ul>"
for post in posts:
html += f"<li><a href='/post/{post['id']}'>{post['title']}</a></li>"
html += "</ul>"
return html
@app.route('/post/<int:post_id>')
def show_post(post_id):
post = next((p for p in posts if p['id'] == post_id), None)
if post:
return f"""
<h1>{post['title']}</h1>
<p><em>{post['date']}</em></p>
<p>{post['content']}</p>
<a href='/posts'>Back to posts</a>
"""
return "Post not found", 404
if __name__ == '__main__':
app.run(debug=True)
Example 2: API Endpoints
from flask import Flask, jsonify, request
app = Flask(__name__)
# Mock user database
users = [
{'id': 1, 'name': 'Alice', 'email': 'alice@example.com'},
{'id': 2, 'name': 'Bob', 'email': 'bob@example.com'},
]
@app.route('/api/users', methods=['GET'])
def get_users():
return jsonify(users)
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = next((u for u in users if u['id'] == user_id), None)
if user:
return jsonify(user)
return jsonify({'error': 'User not found'}), 404
@app.route('/api/users', methods=['POST'])
def create_user():
if not request.is_json:
return jsonify({'error': 'Request must be JSON'}), 400
data = request.get_json()
if 'name' not in data or 'email' not in data:
return jsonify({'error': 'Name and email required'}), 400
new_user = {
'id': len(users) + 1,
'name': data['name'],
'email': data['email']
}
users.append(new_user)
return jsonify(new_user), 201
if __name__ == '__main__':
app.run(debug=True)
Example 3: File Server
from flask import Flask, send_from_directory, safe_join
import os
app = Flask(__name__)
# Serve static files
@app.route('/static/<path:filename>')
def serve_static(filename):
return send_from_directory('static', filename)
# Serve files with security
@app.route('/files/<path:filename>')
def serve_file(filename):
# Security check - only allow certain file types
if not filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.pdf')):
return "File type not allowed", 403
# Safe path joining to prevent directory traversal
safe_path = safe_join('uploads', filename)
if safe_path and os.path.exists(safe_path):
return send_from_directory('uploads', filename)
return "File not found", 404
@app.route('/')
def home():
return """
<h1>File Server</h1>
<ul>
<li><a href="/static/logo.png">View logo</a></li>
<li><a href="/files/document.pdf">View document</a></li>
</ul>
"""
if __name__ == '__main__':
app.run(debug=True)
Best Practices
1. Use Virtual Environments
# Always use virtual environments
python -m venv venv
venv\Scripts\activate # Windows
pip install flask
2. Enable Debug Mode in Development
if __name__ == '__main__':
app.run(debug=True) # Only in development!
3. Use Proper Configuration
# config.py
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret')
DEBUG = os.environ.get('DEBUG', 'False').lower() == 'true'
app.config.from_object(Config())
4. Handle Errors Gracefully
@app.errorhandler(404)
def not_found(error):
return "Page not found", 404
@app.errorhandler(500)
def internal_error(error):
return "Internal server error", 500
5. Log Important Events
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@app.route('/login')
def login():
logger.info("User login attempt")
# ... login logic
Practice Exercises
Exercise 1: Personal Website
Create a Flask app with these routes:
/- Home page with your name and bio/projects- List of your projects/contact- Contact information/hobbies- Your hobbies and interests
Exercise 2: Calculator API
Build a calculator with these endpoints:
GET /add/<int:a>/<int:b>- Add two numbersGET /multiply/<int:a>/<int:b>- Multiply two numbersPOST /calculate- Accept JSON with operation and numbers
Exercise 3: URL Shortener
Create a simple URL shortener:
POST /shorten- Accept a long URL, return short codeGET /<short_code>- Redirect to original URL- Store URLs in memory (dictionary)
Exercise 4: Weather App
Build a weather app (using mock data):
/weather/<city>- Show weather for a city/forecast/<city>- Show 5-day forecast- Include temperature, conditions, humidity
Summary
Flask basics give you the foundation for web development:
Core Concepts:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Hello, World!"
if __name__ == '__main__':
app.run(debug=True)
Key Features:
- Routes connect URLs to functions
- Dynamic routes with variables
- Multiple HTTP methods support
- Different response types (HTML, JSON)
- Configuration management
- Error handling
Best Practices:
- Use virtual environments
- Enable debug mode in development
- Handle errors gracefully
- Log important events
- Use proper configuration
Next: Routes and Views - handling complex URL patterns! ๐