Expanding and Using Flask App
In this tutorial, we will expand on our basic Flask application by adding more features and organizing the code for better scalability and maintainability. We will cover:
- Modularizing the Flask app
- Adding data persistence with a database
- Implementing authentication
- Using Blueprints for better organization
- Testing the Flask app
Step 1: Modularizing the Flask Appβ
As your Flask application grows, organizing your code into multiple files and modules will make it easier to manage.
Create a Project Structureβ
Hereβs a recommended structure for a larger Flask application:
/my_flask_app
/app
__init__.py
models.py
routes.py
config.py
/tests
test_routes.py
run.py
__init__.py
β
This file initializes the Flask application and brings together components.
from flask import Flask
from .config import Config
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
from .routes import main_bp
app.register_blueprint(main_bp)
return app
config.py
β
Store configuration settings here.
class Config:
SECRET_KEY = 'your_secret_key'
SQLALCHEMY_DATABASE_URI = 'sqlite:///site.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
models.py
β
Define your data models using SQLAlchemy.
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
routes.py
β
Define your routes and handlers.
from flask import Blueprint, jsonify, request
from .models import db, User
main_bp = Blueprint('main', __name__)
@main_bp.route('/users', methods=['GET'])
def get_users():
users = User.query.all()
return jsonify([user.as_dict() for user in users]), 200
@main_bp.route('/users', methods=['POST'])
def add_user():
if not request.json:
return jsonify({"error": "No input data provided"}), 400
data = request.json
new_user = User(name=data['name'], email=data['email'])
db.session.add(new_user)
db.session.commit()
return jsonify(new_user.as_dict()), 201
# Add methods for updating and deleting users similarly
run.py
β
Entry point to run the application.
from app import create_app, db
app = create_app()
if __name__ == '__main__':
with app.app_context():
db.create_all() # Create database tables
app.run(debug=True)
Step 2: Adding Data Persistence with a Databaseβ
To persist data, youβll use a database. Flask integrates well with SQLAlchemy, a powerful ORM for Python.
Install SQLAlchemyβ
pip install Flask-SQLAlchemy
Configure SQLAlchemyβ
Add the database URI to config.py
and initialize SQLAlchemy in __init__.py
.
Define Modelsβ
Define your data models in models.py
as shown above. Use db.create_all()
in run.py
to create the database tables.
Use Models in Routesβ
Access and manipulate data using SQLAlchemyβs ORM capabilities in your route handlers.
Step 3: Implementing Authenticationβ
Authentication ensures that only authorized users can access certain resources.
Install Flask-Loginβ
pip install Flask-Login
Configure Flask-Loginβ
Set up Flask-Login in __init__.py
.
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.login_view = 'login'
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
login_manager.init_app(app)
from .routes import main_bp
app.register_blueprint(main_bp)
return app
Create User Loaderβ
In models.py
, add a user loader function.
from flask_login import UserMixin
class User(db.Model, UserMixin):
# Define model fields
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
Implement Authentication Routesβ
Add routes for login and logout in routes.py
.
from flask_login import login_user, logout_user, login_required, current_user
@main_bp.route('/login', methods=['POST'])
def login():
data = request.json
user = User.query.filter_by(email=data['email']).first()
if user and user.password == data['password']: # Hash and check passwords in practice
login_user(user)
return jsonify({"message": "Login successful"}), 200
return jsonify({"error": "Invalid credentials"}), 401
@main_bp.route('/logout', methods=['POST'])
@login_required
def logout():
logout_user()
return jsonify({"message": "Logout successful"}), 200
Step 4: Using Blueprints for Better Organizationβ
Blueprints help in organizing routes and functionality into modular components.
Define Blueprintsβ
In routes.py
, youβve already defined a blueprint. Use blueprints to separate different sections of your application.
Register Blueprintsβ
Register blueprints in create_app()
function in __init__.py
.
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
login_manager.init_app(app)
from .routes import main_bp
app.register_blueprint(main_bp, url_prefix='/api')
return app
Step 5: Testing the Flask Appβ
Testing ensures your application behaves as expected and helps prevent bugs.
Write Testsβ
Create tests in the tests
directory. For example, test_routes.py
might look like this:
import unittest
from app import create_app, db
class BasicTests(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.app.config['TESTING'] = True
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
self.client = self.app.test_client()
with self.app.app_context():
db.create_all()
def tearDown(self):
with self.app.app_context():
db.drop_all()
def test_get_users(self):
response = self.client.get('/api/users')
self.assertEqual(response.status_code, 200)
def test_add_user(self):
response = self.client.post('/api/users', json={'name': 'John Doe', 'email': 'john@example.com'})
self.assertEqual(response.status_code, 201)
self.assertIn(b'John Doe', response.data)
if __name__ == '__main__':
unittest.main()
Run Testsβ
Execute tests with:
python -m unittest discover
Conclusionβ
In this tutorial, we've expanded our basic Flask application by modularizing the code, adding data persistence with SQLAlchemy, implementing authentication, using blueprints for better organization, and writing tests. These steps will help you build more robust, scalable, and maintainable Flask applications.
In the next chapter, we'll explore using Django Rest Framework (DRF) for building REST APIs, another powerful option for Python developers. Stay tuned!