#25 Incompatibilité du modèle forum avec SQLAlchemy

Closed
opened 3 months ago by Lephenixnoir · 5 comments

Le modèle actuel du forum comporte une classe de base, Post, qui représente les contenus datés avec un auteur.

De cette classe héritent deux classes majeures, Thread et Comment. La première représente tous les fils de discussions, qui sont omniprésents sur le site : sujets, programmes, tutoriels, et d’autres. La seconde représente un commentaire normal.

Cet héritage est polymorphe, ce qui permet de charger dynamique des Post et d’obtenir comme on le veut des fils de discussions et des commentaires mélangés.

De Thread héritent encore plusieurs classes, qui sont comme attendu Topic, Program, Tutorial et d’autres. Cela forme un deuxième niveau d’héritage polymorphe.

Le problème est que SQLAlchemy ne supporte pas l’héritage polymorphe en cascade. Accessoirement, c’est assez compliqué et donc difficile à gérer. Voici, pour référence, ce qu’en dit la documentation :

Currently, only one discriminator column or SQL expression may be configured for the entire inheritance hierarchy, typically on the base- most class in the hierarchy. “Cascading” polymorphic discriminator expressions are not yet supported.

-- Mapping Class Inheritance Hierarchies

Pour que ce modèle soit implémentable, il faut donc éliminer un de ces deux héritages polymorphes. Je vois plusieurs solutions.

  1. Éliminer l’héritage PostThread, Comment
    C’est le plus naturel a priori, puisque cet héritage propage bien moins d’information que l’autre. Il faut alors inclure l’information de date et d’auteur autrement que par héritage, par exemple en reproduisant les attributs (duplication de la fonctionnalité) ou en définissant Post comme une sorte de mixin (à voir comment).

  2. Éliminer l’héritage ThreadTopic, Program, Tutorial, etc
    Ici, comme il est hors de question de répliquer la fonctionnalité des fils de discussion, la méthode qui me semble la plus naturelle pour éliminer l’héritage est simplement de mettre en relation chacune de sous-classes avec son fil sous-jacent. Cela se combine très élégamment avec notre volonté de lier le même fil à des topics et programmes jumeaux (même projet) et je pense donc que c’est la meilleure réponse pour l’instant.

Une troisième solution m’avait effleuré l’esprit, mais je l’ai oubliée en rédigeant… ça me reviendra sans doute si la discussion prend du temps.

@Darks Opinion désirée.

Le modèle actuel du forum comporte une classe de base, `Post`, qui représente les contenus datés avec un auteur. De cette classe héritent deux classes majeures, `Thread` et `Comment`. La première représente tous les fils de discussions, qui sont omniprésents sur le site : sujets, programmes, tutoriels, et d'autres. La seconde représente un commentaire normal. Cet héritage est polymorphe, ce qui permet de charger dynamique des `Post` et d'obtenir comme on le veut des fils de discussions et des commentaires mélangés. De `Thread` héritent encore plusieurs classes, qui sont comme attendu `Topic`, `Program`, `Tutorial` et d'autres. Cela forme un deuxième niveau d'héritage polymorphe. Le problème est que **SQLAlchemy ne supporte pas l'héritage polymorphe en cascade**. Accessoirement, c'est assez compliqué et donc difficile à gérer. Voici, pour référence, ce qu'en dit la documentation : > Currently, **only one discriminator column or SQL expression may be configured for the entire inheritance hierarchy**, typically on the base- most class in the hierarchy. “Cascading” polymorphic discriminator expressions are not yet supported. > > -- [Mapping Class Inheritance Hierarchies](https://docs.sqlalchemy.org/en/13/orm/inheritance.html) Pour que ce modèle soit implémentable, il faut donc éliminer un de ces deux héritages polymorphes. Je vois plusieurs solutions. 1. **Éliminer l'héritage `Post` → `Thread`, `Comment`** C'est le plus naturel *a priori*, puisque cet héritage propage bien moins d'information que l'autre. Il faut alors inclure l'information de date et d'auteur autrement que par héritage, par exemple en reproduisant les attributs (duplication de la fonctionnalité) ou en définissant `Post` comme une sorte de *mixin* (à voir comment). 2. **Éliminer l'héritage `Thread` → `Topic`, `Program`, `Tutorial`, etc** Ici, comme il est hors de question de répliquer la fonctionnalité des fils de discussion, la méthode qui me semble la plus naturelle pour éliminer l'héritage est simplement de mettre en relation chacune de sous-classes avec son fil sous-jacent. Cela se combine très élégamment avec notre volonté de lier le même fil à des topics et programmes jumeaux (même projet) et je pense donc que c'est la meilleure réponse pour l'instant. Une troisième solution m'avait effleuré l'esprit, mais je l'ai oubliée en rédigeant... ça me reviendra sans doute si la discussion prend du temps. @Darks Opinion désirée.
Lephenixnoir self-assigned this 3 months ago
Lephenixnoir added the
bug
label 3 months ago
Lephenixnoir added the
proposal
label 3 months ago
Lephenixnoir commented 3 months ago
Owner

J’ajoute que la deuxième solution que je propose revient exactement à implémenter le mécanisme usuel de polymorphisme de SQLAlchemy, Joined Table Inheritance, mais à la main.

En fait on casse la boîte de l’héritage de SQLAlchemy pour aller voir au niveau en-dessous, celui avec les relations. Et c’est parfait pour nous car on planifie justement d’aller toucher à ce niveau pour implémenter la capacité pour un sujet et un programme de partager le même fil de discussion.

Avec les enjeux vraiment en tête, pour moi ça ne fait pas un pli. Compte tenu de la simplification du code et des modèles, je nous estime gagnants avec la seconde solution.

J'ajoute que la deuxième solution que je propose revient exactement à implémenter le mécanisme usuel de polymorphisme de SQLAlchemy, *Joined Table Inheritance*, mais à la main. En fait on casse la boîte de l'héritage de SQLAlchemy pour aller voir au niveau en-dessous, celui avec les relations. Et c'est parfait pour nous car on planifie justement d'aller toucher à ce niveau pour implémenter la capacité pour un sujet et un programme de partager le même fil de discussion. Avec les enjeux vraiment en tête, pour moi ça ne fait pas un pli. Compte tenu de la simplification du code et des modèles, je nous estime gagnants avec la seconde solution.
Darks commented 3 months ago
Owner

J’ai pas le recul nécessaire pour faire un choix. Je te fais confiance sur ce coup là, c’est toi qui a bossé sur le sujet plus que moi.

Enfin, c’est con que SQLAlchemy implémente pas un polymorphisme plus complet…

J'ai pas le recul nécessaire pour faire un choix. Je te fais confiance sur ce coup là, c'est toi qui a bossé sur le sujet plus que moi. Enfin, c'est con que SQLAlchemy implémente pas un polymorphisme plus complet…
Eragon commented 3 months ago
Collaborator

Ayant des problèmes à comprendre SQlAlchemy, je n’ai que compris que ce dernier n’implémentait pas une/des fonctionnalités utilisés dans l’architecture du modèle.

Sans rapport: C’est déjà un miracle que j’ai pigé ça, moi je pige que dalle à ce qui touche aux BDD, car j’ai pas assez bossé le sujet. À ce propos vous auriez-pas un tuto à me conseiller que je comprenne mieux de quoi on parle ici ?

Ayant des problèmes à comprendre SQlAlchemy, je n'ai que compris que ce dernier n'implémentait pas une/des fonctionnalités utilisés dans l'architecture du modèle. Sans rapport: C'est déjà un miracle que j'ai pigé ça, moi je pige que dalle à ce qui touche aux BDD, car j'ai pas assez bossé le sujet. À ce propos vous auriez-pas un tuto à me conseiller que je comprenne mieux de quoi on parle ici ?
Darks commented 3 months ago
Owner

Ce que l’on appelle « polymorphisme », c’est l’héritage. À savoir qu’un objet peut avoir plusieurs formes (être un Topic, un Thread et un Post en même temps).

SQLAlchemy ne sait pas gérer le polymorphisme de niveau 3 et plus : ton objet ne peut pas être une instance de 3 classes en même temps. Or, puisque PostThreadTopic, il nous faut casser l’héritage à un endroit.

Il y en a deux possibles : entre Post et Thread ou entre Thread et Topic. D’où la question de Lephe pour savoir à quel endroit casser.

Ce que l'on appelle « polymorphisme », c'est l'héritage. À savoir qu'un objet peut avoir plusieurs formes (être un `Topic`, un `Thread` et un `Post` en même temps). SQLAlchemy ne sait pas gérer le polymorphisme de niveau 3 et plus : ton objet ne peut pas être une instance de 3 classes en même temps. Or, puisque `Post` → `Thread` → `Topic`, il nous faut casser l'héritage à un endroit. Il y en a deux possibles : entre `Post` et `Thread` ou entre `Thread` et `Topic`. D'où la question de Lephe pour savoir à quel endroit casser.
Lephenixnoir commented 3 months ago
Owner

Bon, du coup je suis dessus. Voici les modifications que je vais faire :

  • Lier les sujets, programmes, tutoriels aux fils de discussion via une relation.
  • Faire hériter les sujets, programmes, tutoriels de la classe Post au lieu de faire hériter les fils de discussion. En effet la classe Post sert à dater et désigner un auteur. Or un sujet et un programme partageant le même fil de discussion ont quand meêm des dates de création différentes !
  • Retirer l’attribut text de la classe Post, qui n’avait pas grand-chose à faire là.

Avec ça on sera prêts à partir o/

Bon, du coup je suis dessus. Voici les modifications que je vais faire : * Lier les sujets, programmes, tutoriels aux fils de discussion via une relation. * Faire hériter les sujets, programmes, tutoriels de la classe `Post` au lieu de faire hériter les fils de discussion. En effet la classe `Post` sert à dater et désigner un auteur. Or un sujet et un programme partageant le même fil de discussion ont quand meêm des dates de création différentes ! * Retirer l'attribut `text` de la classe `Post`, qui n'avait pas grand-chose à faire là. Avec ça on sera prêts à partir o/
Sign in to join this conversation.
No Milestone
No Assignees
3 Participants
Due Date

No due date set.

Dependencies

This issue currently doesn't have any dependencies.

Loading…
Cancel
Save
There is no content yet.