2 12 Modèles
Eragon edited this page 2024-03-14 14:35:26 +01:00

Les modèles (dans /app/models) représentent tous les contenus qui se trouvent dans la base de données. Généralement, une classe correspond à une table avec des attributs. Cette page du wiki décrit les relations entre les différentes parties du modèle, ainsi que les conventions sur le code.

Schéma du modèle complet

À date de 19d90c6845 (14 Avril 2022) :

(diagramme UML des classes de données)

  • Les classes en bleu sont les classes standard/banales.
  • Les classes en vert sont les classes de contenus principaux, qui héritent de Post (Comment ne compte pas).
  • Les classes en blanc sont les parties non implémentées ou encore en discussion.

Gestion des Post et contenus principaux

La première version du modèle voulait faire de l'héritage polymorphe en cascade, mais ce n'est pas pas supporté par SQLAlchemy, d'où la forme actuellement qui est simplifiée.

Architecture générale

Un Post est soit un commentaire soit un des « contenus principaux » du site (topics, programmes, tutoriels, assets graphiques essentiellement). Tout ce qu'un utilisateur crée comme contenu est donc un Post, et cette classe traque les dates et auteurs.

La classe Thread représente un fil de commentaires liant des Comment entre eux. Le top_comment peut être changé dynamiquement.

Les classes de type Topic, Program possèdent un attribut vers le Thread leur étant associé, qui est unique (les Thread ne sont pas partagés).

Promotion des topics

La v5 a une idée assez centrale d'avoir un seul fil de discussion par projet, avec tout le contexte nécessaire du projet. En particulier, avoir à la fois un topic et les commentaires d'un programme sur la v4 est assez courant et assez casse-pieds.

Le système de promotion de topics revient à dire qu'un programme c'est juste un topic avec des téléchargements, qu'un tutoriel c'est juste un topic avec des outils spéciaux pour visualiser, etc. On s'autorise donc à convertir un topic en programme/tutoriel/asset graphique lorsque le contenu est publié.

Pour ne pas casser les liens vers le topic, cette promotion est enregistrée dans le champ promotion_id du topic et une redirection est mise en place. Il faut donc toujours vérifier promotion_id quand on travaille avec un topic.

Post principaux

Chaque Thread possèdant un top_comment, les Topic, Program, etc. n'ont plus d'attribut particulier contenant le post principal. Cette souplesse sur le top_comment permet de changer dynamiquement le post principal, eg. de poster un nouveau commentaire et de le désigner comme post principal sans perdre l'ancien (qui est important pour avoir le contexte des commentaires précédents).

Conventions sur le code

Références à d'autres modèles

Pour faire référence à un autre objet de la base de données, comme par exemple un topic fait référence au forum dans lequel il se trouve, créez deux champs : l'entier servant à identifier l'ID distant et la relation servant à l'utiliser sous forme d'objet.

# Parent forum
forum_id = db.Column(db.Integer, db.ForeignKey('forum.id'), nullable=False)
forum    = db.relationship('Forum', backref='topics',foreign_keys=forum_id)

Une fois que c'est fait, utilisez toujours la version objet. Vous ne devez jamais assigner une valeur à forum_id ou prendre un ID entier en paramètre d'une fonction. Il suffit d'écrire my_topic.forum = my_forum pour assigner l'attribut.

Utiliser backref et non back_populates

Pour les relations one-to-one et one-to-many, on place le champ d'ID et la relation d'un côté one (n'importe lequel), et on utilise backref pour créer un attribut dans la classe référencée si on veut pouvoir lire la relation dans l'autre sens. Dans l'exemple ci-dessus, on a ajouté un attribut topics à la classe Forum, ce qui est signalé par un commentaire côté Forum :

# Other fields populated automatically through relations:
#   <topics>   List of topics in this exact forum (of type Topic)

Avec back_populates, il faut ajouter une relation dans les deux classes, ce qui est moins pratique parce que :

  • Il y a plus de champs et les deux relations doivent 99% du temps avoir les même paramètres donc il faut assurer la cohérence de deux versions des paramètres.
  • Il ne faut ajouter de champ id que d'un côté, ce qui augmente encore la confusion. Avec backref il y a toujours une paire ID/objet et on ne se pose pas de questions.

Relations many-to-many

Pour créer une relation many-to-many, il faut une table intermédiaire et des secondary dans les relations. Il y a plusieurs exemples dans le code, voyez par exemple GroupMember et GroupPrivilege (app/models.privs.py) pour un exemple simple et un exemple avec des propriétés supplémentaires, respectivement.

Ne pas importer les modèles pour les références

Le premier argument de db.relationship() est une chaîne de caractères qui est résolue dynamiquement. Il n'y a pas besoin d'importer le modèle correspondant pour l'utiliser (par exemple Forum dans le premier exemple), ce qui poserait plein de problèmes de dépendances circulaires.

Ne pas utiliser db.session

Les méthodes de modèles n'ont aucune raison d'appeller db.session pour sauvegarder des changements dans la base de données. La route qui s'exécute se charge d'ajouter les objets et de les sauvegarder au moment opportun.