models.py devient énorme, quelle est la meilleure façon de le casser?

91

Instructions de mon superviseur: "Je veux éviter de mettre de la logique dans le models.py. À partir de maintenant, utilisons cela comme seules classes pour accéder à la base de données, et gardons toute la logique dans les classes externes qui utilisent les classes de modèles, ou les encapsulent."

J'ai l'impression que ce n'est pas la bonne voie à suivre. Je pense que garder la logique hors des modèles juste pour garder le fichier petit est une mauvaise idée. Si la logique est la meilleure dans le modèle, c'est là qu'elle devrait vraiment aller quelle que soit la taille du fichier.

Alors, y a-t-il un moyen simple d'utiliser uniquement des includes? En PHP-speak, j'aimerais proposer au superviseur que nous ayons juste d' models.pyinclure () les classes modèles d'autres endroits. Conceptuellement, cela permettrait aux modèles d'avoir toute la logique que nous voulons, tout en réduisant la taille des fichiers en augmentant le nombre de fichiers (ce qui conduit à moins de problèmes de contrôle de révision comme les conflits, etc.).

Alors, existe-t-il un moyen simple de supprimer les classes de modèle du fichier models.py, mais que les modèles fonctionnent toujours avec tous les outils Django? Ou, y a-t-il une solution complètement différente mais élégante au problème général d'un "gros" fichier models.py? Toute contribution serait appréciée.

Eddifié
la source
7
Vous connaissez la déclaration d'importation, non?
balpha
7
PS. Je ne veux pas dire ça de manière offensive, je veux juste savoir où vous en êtes.
balpha
1
Oui, mais je ne savais pas si les outils d'administration de django fonctionneraient simplement en utilisant des instructions d'importation pour extraire les modèles. Je préférerais demander ici plutôt que de passer beaucoup de temps à essayer d'utiliser des importations ole simples juste pour découvrir que les outils de django ne fonctionnent pas bien avec eux. J'avoue que je suis plus récent en python et django, donc je ne suis probablement qu'à une simple compréhension de la déclaration d'importation ...
Modifié le

Réponses:

64

Django est conçu pour vous permettre de créer de nombreuses petites applications au lieu d'une seule grande application.

À l'intérieur de chaque grande application se trouvent de nombreuses petites applications qui ont du mal à être gratuites.

Si vous models.pyvous sentez grand, vous en faites trop. Arrêtez. Se détendre. Décomposer.

Trouvez de petits composants ou pièces d'application plus petits et potentiellement réutilisables. Vous ne devez pas réellement les réutiliser. Pensez simplement à eux comme potentiellement réutilisables.

Considérez vos chemins de mise à niveau et décomposez les applications que vous voudrez peut-être remplacer un jour. Vous ne devez pas réellement remplacer, mais vous pouvez les considérer comme un « module » autonome de programmation qui pourrait se remplacer par quelque chose de plus frais à l'avenir.

Nous avons une douzaine d'applications, chacune model.pyne dépassant pas environ 400 lignes de code. Ils sont tous assez concentrés sur moins d'une demi-douzaine de définitions de classe distinctes. (Ce ne sont pas des limites strictes, ce sont des observations sur notre code.)

Nous nous décomposons tôt et souvent.

S.Lott
la source
1
juste sur le point. toute application Web non triviale serait plusieurs petites «applications». prenez un indice de la contribution et d'autres applications populaires, l'authentification des utilisateurs est une application, le marquage en est une autre, les profils d'utilisateurs un de plus, etc.
Javier
4
Bien que ce soit la «bonne» façon et utile de savoir, ce n'est pas tout à fait ce que je recherchais. Je m'excuse s'il n'y avait aucun moyen de savoir quel type de réponse je cherchais. :)
Édité le
@Eddified: si vous ne le faites pas, cela ne fera qu'empirer. Commencez à vous séparer maintenant.
S.Lott
Assez drôle, en ce moment même, j'écoute Jacob Kaplan Moss (chez OSCON) expliquer exactement cela dans des détails très justifiés ;-).
Alex Martelli
13
La réponse de Glenn Maynard est bien meilleure sur celle-ci. Diviser une application Web complexe en de nombreuses applications est certainement une bonne pratique, mais il en va de même pour la refactorisation d'un fichier model.py DANS une application. Les deux actions peuvent être orthogonales.
Erik
108

Il est naturel que les classes de modèle contiennent des méthodes pour opérer sur le modèle. Si j'ai un modèle Book, avec une méthode book.get_noun_count(), c'est là qu'il appartient - je ne veux pas avoir à écrire " get_noun_count(book)", à moins que la méthode n'appartienne en fait intrinsèquement à un autre package. (Cela pourrait - par exemple, si j'ai un package pour accéder à l'API d'Amazon avec " get_amazon_product_id(book)".)

J'ai grincé des dents lorsque la documentation de Django a suggéré de mettre les modèles dans un seul fichier, et j'ai pris quelques minutes dès le début pour comprendre comment le diviser en un sous-paquet approprié.

site/models/__init__.py
site/models/book.py

__init__.py ressemble à:

from .book import Book

donc je peux toujours écrire "à partir du livre d'importation site.models".


Ce qui suit n'est requis que pour les versions antérieures à Django 1.7, voir https://code.djangoproject.com/ticket/3591

La seule astuce est que vous devez définir explicitement l'application de chaque modèle, en raison d'un bogue dans Django: cela suppose que le nom de l'application est la troisième-dernière entrée dans le chemin du modèle. "site.models.Book" donne "site", ce qui est correct; "site.models.book.Book" fait penser que le nom de l'application est "models". C'est un hack assez méchant de la part de Django; il devrait probablement rechercher dans la liste des applications installées une correspondance de préfixe.

class Book(models.Model):
    class Meta: app_label = "site"

Vous pourriez probablement utiliser une classe de base ou une métaclasse pour généraliser cela, mais je ne me suis pas encore préoccupé de cela.

Glenn Maynard
la source
2
+1 J'ai utilisé ceci avec succès. Bien que S. Lott ait raison de dire que plusieurs applications sont une bonne idée, c'est la solution ici et maintenant.
Alexander Ljungberg
35
Je ne vois pas beaucoup d'avantages à diviser les choses en un tas d'applications, lorsque vos modèles sont étroitement et intrinsèquement liés.
Glenn Maynard
2
Cela m'intéresse. J'ai lu le lien du wiki de django scompt posté et j'ai trouvé ceci: "Cela a été vérifié pour fonctionner sans la classe Meta app_labels, dans la branche principale actuelle." Cela signifie-t-il que si vous travaillez avec la branche principale, nous pouvons supprimer les éléments Meta: app_label? C'est déroutant car c'est après le commentaire sur le ticket pour résoudre ce problème.
Dan.StackOverflow
2
Je viens de tester avec le coffre (plus tôt aujourd'hui, r11286); si l'app_name n'est pas défini, le modèle n'apparaît tout simplement pas dans "sqlall appname" et ne sera probablement pas créé par syncdb (mais je ne l'utilise pas donc je ne peux pas le tester). C'est un cas d'erreur assez déroutant, car il ne déclenche aucune erreur; il n'apparaît pas en silence.
Glenn Maynard
2
Wow, près de 10 ans plus tard et j'aime toujours cette solution. J'ai convenu que c'était une bien meilleure approche que de diviser votre code en applications plus petites, ce qui, à mon avis, peut conduire à une base de code sur laquelle il est difficile de raisonner.
Michael Hays
5

Je ne peux pas vraiment comprendre lequel des nombreux problèmes que vous pourriez avoir. Voici quelques possibilités avec des réponses:

  • plusieurs modèles dans le même fichier

    Mettez-les dans des fichiers séparés. S'il existe des dépendances, utilisez l'importation pour extraire les modèles supplémentaires.

  • fonctions logiques / utilitaires étrangères dans models.py

    Mettez la logique supplémentaire dans des fichiers séparés.

  • méthodes statiques de sélection de certaines instances de modèle dans la base de données

    Créez un nouveau gestionnaire dans un fichier séparé.

  • méthodes évidemment liées au modèle

    save, __unicode__ et get_absolute_url sont des exemples.

Hughdbrown
la source