Au fil du temps, les fichiers models.py au sein d’un projet Django ont tendance à grossir, devenant compliqués à relire et à maintenir. C’est particulièrement vrai si vos models contiennent de la logique métier. Si il est généralement une bonne chose d’éviter d’avoir des “fat models” dans un projet Django (mais ce sujet mériterait un article dédié) il est possible de commencer par découper un gros fichier de models en plusieurs fichiers plus petits en le transformant en un package Python.
Cette astuce peut vous aider à nettoyer (un peu) votre base de code!
L’exemple
Disons que vous avez une app blog dans votre projet Django. La structure de cette app ressemble à ça:
.
├── blog
| ├── __init__.py
| ├── models.py
| ├── tests.py
| ├── views.py
et le fichier models.py contient les models suivants:
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User)
class Comment(models.Model):
article = models.ForeignKey(Article)
content = models.TextField()
author = models.ForeignKey(User)
Utilisation de strings pour références les models dans les champs de relation
La première chose à faire est d’utiliser le nom de vos models, au lieu de directement utiliser vos models, dans vos champs de relation (tels que ForeignKey, ManyToManyField, etc …). Cela devrait réduire pour vous le risque de rencontrer un problème d’import circulaire lors de la création du package Python. Nous modifions donc la déclaration du champs Comment.article de la manière suivante:
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User)
class Comment(models.Model):
article = models.ForeignKey("Article") # Nous référençons le nom du model au lieu du model en lui même.
content = models.TextField()
author = models.ForeignKey(User)
Veuillez noter que:
- Référencer la classe
Usern’est pas un problème car elle provient du packagedjango. - Si vous avez besoin de référencer un model provenant d’une autre de vos applications, vous pouvez le faire en préfixant son nom par le nom de l’application (par exemple:
blog.Article).
Transformez votre fichier models en un package Python
Vous pouvez désormais créer un package Python, nommé models, dans votre application Django. Ce package doit contenir les fichiers suivants:
__init__.pyarticle.pycomment.py
.
├── blog
| ├── __init__.py
| ├── models
| | ├── __init__.py
| | ├── article.py
| | ├── comment.py
| ├── models.py
| ├── tests.py
| ├── views.py
Maintenant, copiez vos models dans ces nouveaux fichiers:
# blog/models/article.py
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User)
# blog/models/comment.py
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Comment(models.Model):
article = models.ForeignKey("Article") # Reference the name of the model instead of the model itself.
content = models.TextField()
author = models.ForeignKey(User)
Vous pouvez maintenant supprimer votre ancien fichier models.py!
A cette étape, si vous executez la commande python manage.py check, Django devrait vous alerter sur le fait qu’il n’est plus capable de trouver vos modèles.
Tout ce que nous avons à faire pour finaliser notre nouveau package Python models, et permettre à Django de découvrir nos models, est d’exporter les models en utilisant son fichier __init__.py:
# blog/models/__init__.py
from .article import Article
from .comment import Comment
__all__ = ["Article", "Comment"]
En Python, le mot clé __all__ défini la liste des objets publiques d’un module (un module peut être un fichier python ou bien un package). Il expose une liste de str qui définissent les éléments au sein d’un module qui seront exportés lorsque from <module> import * est utilisé sur le module.
Le code plus haut:
- Permet à Django de découvrir nos models de la même façon qu’il le fait avec un fichier
models.py. - Vous pouvez toujours écrire
from blog.models import Articledonc le changement est totalement transparent pour Django. - Vous pouvez aussi écrire
from blog.models.article import Article. En Python, rien n’est réellement privé.
Conclusion
J’espère que vous aurez appris quelque chose aujourd’hui. J’utilise cette manière de structurer mes fichiers de models sur pratiquement tous les projets sur lesquels je travaille (qu’ils soient anciens ou nouveaux). Cette manière de faire peut aussi facilement être appliquée à d’autres ressources telles que:
- forms
- model admin
- views
- etc …
Vous pouvez aussi appliquer cette technique à vos fichiers de tests. A titre d’exemple, les tests unitaires liés à notre model Article pourraient être placés dans un fichier test_article.py. Cette convention rends plus simple de savoir si notre fichier est déjà couvert par des tests unitaires ou non.