Ho un'applicazione Flask con decine di modelli complessi, quasi tutti correlati tra loro.
Un semplice pseudo-schema di alcuni di essi:
+----------------+
| FoodGroup |
+-------+--------+
^
|
+-------+--------+
| Food +<------+
+---+------------+ | +--------------+
^ | | NutrientGroup|
| | +-------^------+
| | |
| | |
+---+----------------+ +---------------+ +------+-----+
| FoodMeasurements | | FoodData +------>+ Nutrient |
+--------------------+ +---------------+ +------------+
Il mio obiettivo è disgiungere il più possibile la logica aziendale dai modelli ORM e incapsulare le operazioni dei dati in modo semantico e sicuro. Molte operazioni richiedono molta logica da molti modelli e voglio evitare il mixaggio diretto dei modelli ORM. Il primo pensiero è stato quello di creare un modulo di servizi e riempirlo con funzioni come altri suggerivano . Preferisco un approccio più OOP, quindi ho implementato un livello Manager che sarà responsabile delle operazioni sui dati.
ad es. non toccherò direttamente la FoodMeasurement. A FoodMeasurement appartiene al cibo. Allo stesso modo, FoodData appartiene anche al cibo e ha una relazione nutriente. Ho bisogno di un FoodManager per modificare i loro valori. (Nello schema db reale, Food è correlato a 9 tabelle.)
Al codice seguente proverò a spiegare il concetto. (Non è il codice originale, quindi scusami per errori e refusi.)
class BaseManager:
def __init__(self, model):
# The base class keeps a reference of the orm model
self.model = model
def save(self):
# code for saving obj to db
...
def create(self, **kwarg):
raise NotImplementedError()
# method which all children should implement
def get_id(self):
# similar methods will be on the children, like get_name etc
return self.model.id
class FoodManager(BaseManager):
def __init__(self, model: Food):
# When the class is initiated it passes the orm model to the
# model property
super().__init__(model)
@classmethod
def create(cls, name: str, food_id: int, food_group_id: int,
enabled: Optional[bool] = True):
# When 'create' method is called it creates a FoodManager
# object with the ORM model assigned to the model property
args = locals()
args.pop("cls", None)
food = Food(**args)
return cls(food)
@classmethod
def get_food(cls, id):
# Get a food by its id
# Similar to 'create' method, but the ORM model comes from
# the db
food = Food.query.filter_by(id=id).first()
return cls(food)
def get_measurements(self):
return self.model.measurements.all()
def get_nutrients(self):
return self.model.nutrients.all()
def set_measurements(self):
# lots of code, logic, validation etc...
self.model.measurements.append(data)
# for chaining
return self
def set_nutrients(self):
#similar to the above
self.model.nutrients.append(data)
return self
def update_measurements(self):
# ... checks for modified FoodMeasurement ORM models
# and acts accordingly ...
return self
# Now I can create a food and save it to db
food = FoodManager.create(name="Banana", food_id=1, food_group_id=1)
food.set_nutrients(data)
food.set_measurements(data_2)
food.save()
# I can do oneliners too
FoodManager.create(**food_data)
\ .set_nutrients(**nutrients).set_measurements(**meas).save()
# Furthermore, I can load a record from the db and do some operations
db_food = FoodManager.get_food(1)
db_food_meas = db_food.get_measurements()
# ... code for edit/remove/add measurements
db_food.update_measurements(db_food_meas).save()
# Finally, if I already have an orm model from another operation
orm_food = Food.query.filter_by(id=id).first()
etc_food = FoodManager(orm_food)
# ...
Raccomanderesti questo modello? Quali difetti incontrerò usando questo modello?