I'd like to build an app in Flask that can switch between talking to a PostGresql and a Mongo DB. As I understand it, the Strategy Pattern is about being able to switch algorithms that are referred to by some method call on the fly. So, I think this pattern would be relevant because I want to switch out what abstract functions like create, read, update, delete do. It sounds to me like strategy would be relevant here, but I'm not quite sure how to implement it. Here's what I'm thinking:
I have this file to handle my routes:
from flask import Blueprint from .models import Entity api = Blueprint('api', __name__, url_prefix='/api') from .models.EntityWrapper import Entity @api.route('/entities', methods=['POST']) def create_entity(): entity = EntityWrapper(label="cheese") entity.save() return jsonify(entity) @api.route('/entities') def get_entities(): entities = Entity.read() return jsonify(entities)
The save and read methods should do different things, depending on which database is being used. The class they belong to,
EntityWrapper, reads the environment variables and selects which kind of Entity class should be exported:
import os from .Entity_mongo import Entity as MongoEntity from .Entity_postgres import Entity as PostgresEntity from dotenv import load_dotenv, find_dotenv load_dotenv(find_dotenv()) DB_TYPE = os.environ.get("DB_TYPE") Entity = None if DB_TYPE == 'mongo': Entity = MongoEntity elif DB_TYPE == 'postgres': Entity = PostgresEntity else: raise Exception("'DB_TYPE' improperly defined in .env")
from .Model_postgres import PostgresModel class Entity(db.Model): __tablename__ = 'entities' id = db.Column(db.Integer, primary_key=True) label = db.Column(db.String(), nullable=False)
Since there are a number of methods that should all be handled "the postgres way" for any object, I'm bundling these into a class called PostgresModel. (A mimilar class would be defined for Mongo, doing this "the mongo way", but for sake of brevity I'll only show postgres).
# from .ModelABC import ModelABC from ..extensions import postgres_db as db # class PostgresModel(ModelABC, db.Model): class PostgresModel(db.Model): def create(self, instance): # do some postgres create stuff db.session.add(instance) db.session.save() def read(self, cls): # do some postgres read stuff cls.query.all() def update(): # do some postgres update stuff pass def delete(): # do some postgres delete stuff pass
I can think of one way the stategy pattern might fit in here:
PostgresModel could inherit from some
ModelABC class, which would keep track of which strategy is currently being used to create, read, update, or delete. It seems to be this class would look something like the following. To tell the truth, I'm not even sure what advantages it could bring, but here's what it might look like:
from abc import ABC, abstractmethod class ModelABC(ABC): def assign_strategy(self, strategy_key): pass def use_strategy(self, strategy, *args, **kwargs): if strategy == 'create': self.create_strategy(*args, **kwargs) elif strategy == 'read': self.read_strategy(*args, **kwargs) elif strategy == 'update': self.update_strategy(*args, **kwargs) elif strategy == 'delete': self.delete_strategy(*args, **kwargs) else: raise Exception('strategy name invalid') @abstractmethod def create_strategy(self): pass @abstractmethod def read_strategy(self): pass @abstractmethod def update_strategy(self): pass @abstractmethod def delete_strategy(self): pass
Am I on the right track with this strategy pattern, or is this not the right use case?