pythondatabaseflaskflask-sqlalchemy

Separate DB Schema from Endpoint


I have a file app.py like this:

import os
from dotenv import load_dotenv
from flask import Flask, jsonify, request
from flask_jwt_extended import JWTManager, jwt_required, get_jwt_identity, create_access_token, create_refresh_token
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_cors import CORS
from datetime import timedelta


load_dotenv()

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('SQLALCHEMY_DATABASE_URI')
app.config['JWT_SECRET_KEY'] = os.getenv('JWT_SECRET_KEY')

db = SQLAlchemy(app)
CORS(app)
jwt = JWTManager(app)

# ------------------------------ DB SCHEMA ------------------------------
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password = db.Column(db.String(50), nullable=False)

class Customer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    email = db.Column(db.String(120), nullable=False)
    phone = db.Column(db.String(120), nullable=False)

# --------------- LOGIN ENDPOINT --------------
@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username', '')
    password = request.json.get('password', '')
    user = User.query.filter_by(username=username).first()

    if user and check_password_hash(user.password, password):
        access_token = create_access_token(identity=username, fresh=True, expires_delta=timedelta(minutes=30))
        refresh_token = create_refresh_token(identity=username)
        return jsonify(access_token=access_token, refresh_token=refresh_token), 200
    
    return jsonify({"message": "Invalid credentials"}), 401

if __name__ == '__main__':
    app.run(debug=True)

I'm trying to separate the DB SCHEMA and the API into two separated file. The problem is that if I create a file containing the DB schema and another one containing the app I have a circular importing error


Solution

  • I would recommend reading the Flask documentation on blueprints: https://flask.palletsprojects.com/en/3.0.x/blueprints/

    Blueprints lets you separate an application's routes from the (possibly global) application object.

    You should also separate the definition and initialization of the SQLAlchemy object, i.e.:

    ### file my_flask_app.py:
    from my_db import db
    from my_blueprint import my_blueprint
    
    app = Flask(__name__)
    db.init_app(app)
    app.register_blueprint(my_blueprint)
    app.run(debug=True)
    
    ### file my_db.py:
    from flask_sqlalchemy import SQLAlchemy
    
    db = SQLAlchemy()
    
    class User(db.Model):
        ...
    
    class Customer(db.Model):
        ...
    
    ### file my_blueprint.py
    from flask import Blueprint
    from my_db import User
    
    my_blueprint = Blueprint('my_blueprint', __name__)
    
    @my_blueprint.route('/login', methods=['POST'])
    def login():
        username = request.json.get('username', '')
        password = request.json.get('password', '')
        user = User.query.filter_by(username=username).first()
    
        if user and check_password_hash(user.password, password):
            access_token = create_access_token(identity=username, fresh=True, expires_delta=timedelta(minutes=30))
            refresh_token = create_refresh_token(identity=username)
            return jsonify(access_token=access_token, refresh_token=refresh_token), 200
        
        return jsonify({"message": "Invalid credentials"}), 401