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")
Here's Entity_posgres.py
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?