553 votes

Séparation de la logique métier et de l'accès aux données dans django

J'écris un projet dans Django et je constate que 80% du code se trouve dans le fichier models.py . Ce code est déroutant et, après un certain temps, je ne comprends plus ce qui se passe réellement.

Voici ce qui me dérange :

  1. Je trouve laid que mon niveau de modèle (qui était censé être responsable uniquement du travail avec les données d'une base de données) est aussi l'envoi d'emails, la marche sur API vers d'autres services, etc.
  2. De plus, je trouve inacceptable de placer la logique métier dans la vue, car de cette façon, il devient difficile de la contrôler. Par exemple, dans mon application, il existe au moins trois façons de créer de nouvelles instances de User mais techniquement, il devrait les créer de manière uniforme.
  3. Je ne remarque pas toujours quand les méthodes et propriétés de mes modèles deviennent non déterministes et quand ils développent des effets secondaires.

Voici un exemple simple. Au début, le User le modèle était comme ça :

class User(db.Models):

    def get_present_name(self):
        return self.name or 'Anonymous'

    def activate(self):
        self.status = 'activated'
        self.save()

Avec le temps, c'est devenu ça :

class User(db.Models):

    def get_present_name(self): 
        # property became non-deterministic in terms of database
        # data is taken from another service by api
        return remote_api.request_user_name(self.uid) or 'Anonymous' 

    def activate(self):
        # method now has a side effect (send message to user)
        self.status = 'activated'
        self.save()
        send_mail('Your account is activated!', '…', [self.email])

Ce que je veux, c'est séparer les entités dans mon code :

  1. Entités de ma base de données, niveau de persistance : Quelles données mon application conserve-t-elle ?
  2. Entités de mon application, niveau logique métier : Que fait mon application ?

Quelles sont les bonnes pratiques pour mettre en œuvre une telle approche qui peut être appliquée dans Django ?

16 votes

Lire à propos des signaux

1 votes

Vous avez supprimé l'étiquette mais vous pourriez utiliser l'ICD pour réaliser la séparation entre ce que le système fait (la fonctionnalité) et ce qu'il est (le modèle de données/domaine).

3 votes

Vous proposez d'implémenter toute la logique commerciale dans des rappels de signaux ? Malheureusement, toute mon application ne peut pas être liée à des événements dans la base de données.

717voto

publysher Points 1923

Il semble que vous demandiez quelle est la différence entre le modèle de données et le modèle de domaine - Le second est l'endroit où vous pouvez trouver la logique commerciale et les entités telles que perçues par votre utilisateur final, le premier est l'endroit où vous stockez réellement vos données.

En outre, j'ai interprété la troisième partie de votre question comme suit : comment remarquer l'absence de séparation de ces modèles ?

Ce sont deux concepts très différents et il est toujours difficile de les séparer. Toutefois, il existe des modèles et des outils communs qui peuvent être utilisés à cette fin.

À propos du modèle de domaine

La première chose que vous devez reconnaître, c'est que votre modèle de domaine ne porte pas vraiment sur les données, mais plutôt sur actions y questions tels que "activer cet utilisateur", "désactiver cet utilisateur", "quels utilisateurs sont actuellement activés ?", et "quel est le nom de cet utilisateur ?". En termes classiques : il s'agit de demandes de renseignements y commandes .

Penser en termes de commandes

Commençons par examiner les commandes de votre exemple : "activer cet utilisateur" et "désactiver cet utilisateur". Ce qui est bien avec les commandes, c'est qu'elles peuvent facilement être exprimées par de petits scénarios de type "donné, quand, alors" :

donné un utilisateur inactif
quand l'administrateur active cet utilisateur
puis l'utilisateur devient actif
y un e-mail de confirmation est envoyé à l'utilisateur
y une entrée est ajoutée au journal du système
(etc. etc.)

De tels scénarios sont utiles pour voir comment différentes parties de votre infrastructure peuvent être affectées par une seule commande - dans ce cas, votre base de données (une sorte de drapeau "actif"), votre serveur de messagerie, votre journal système, etc.

De tels scénarios vous aident également à mettre en place un environnement de développement piloté par les tests.

Enfin, le fait de penser en termes de commandes vous aide réellement à créer une application orientée vers les tâches. Vos utilisateurs l'apprécieront :-)

Expression des commandes

Django fournit deux façons simples d'exprimer les commandes ; ce sont toutes deux des options valables et il n'est pas inhabituel de mélanger les deux approches.

La couche de service

En module de service a déjà été décrit par @Hedde . Ici, vous définissez un module distinct et chaque commande est représentée par une fonction.

services.py

def activate_user(user_id):
    user = User.objects.get(pk=user_id)

    # set active flag
    user.active = True
    user.save()

    # mail user
    send_mail(...)

    # etc etc

Utilisation des formulaires

L'autre moyen est d'utiliser un formulaire Django pour chaque commande. Je préfère cette approche, car elle combine plusieurs aspects étroitement liés :

  • exécution de la commande (que fait-elle ?)
  • validation des paramètres de la commande (peut-elle le faire ?)
  • présentation de la commande (comment faire ?)

forms.py

class ActivateUserForm(forms.Form):

    user_id = IntegerField(widget = UsernameSelectWidget, verbose_name="Select a user to activate")
    # the username select widget is not a standard Django widget, I just made it up

    def clean_user_id(self):
        user_id = self.cleaned_data['user_id']
        if User.objects.get(pk=user_id).active:
            raise ValidationError("This user cannot be activated")
        # you can also check authorizations etc. 
        return user_id

    def execute(self):
        """
        This is not a standard method in the forms API; it is intended to replace the 
        'extract-data-from-form-in-view-and-do-stuff' pattern by a more testable pattern. 
        """
        user_id = self.cleaned_data['user_id']

        user = User.objects.get(pk=user_id)

        # set active flag
        user.active = True
        user.save()

        # mail user
        send_mail(...)

        # etc etc

Penser en termes de requêtes

Votre exemple ne contenait aucune requête, j'ai donc pris la liberté d'inventer quelques requêtes utiles. Je préfère utiliser le terme "question", mais "requête" est la terminologie classique. Les requêtes intéressantes sont : "Quel est le nom de cet utilisateur ?", "Cet utilisateur peut-il se connecter ?", "Montrez-moi une liste des utilisateurs désactivés", et "Quelle est la distribution géographique des utilisateurs désactivés ?".

Avant de vous lancer dans la réponse à ces questions, vous devriez toujours vous poser cette question : est-ce que.. :

  • a présentation une requête juste pour mes modèles, et/ou
  • a logique d'entreprise une requête liée à l'exécution de mes ordres, et/ou
  • a reporting requête.

Les requêtes de présentation sont simplement effectuées pour améliorer l'interface utilisateur. Les réponses aux requêtes de logique métier affectent directement l'exécution de vos commandes. Les requêtes de rapport sont simplement destinées à des fins analytiques et ont des contraintes de temps plus lâches. Ces catégories ne s'excluent pas mutuellement.

L'autre question est : "est-ce que j'ai un contrôle total sur les réponses ?" Par exemple, lorsque nous interrogeons le nom de l'utilisateur (dans ce contexte), nous n'avons aucun contrôle sur le résultat, car nous nous appuyons sur une API externe.

Faire des requêtes

La requête la plus basique dans Django est l'utilisation de l'objet Manager :

User.objects.filter(active=True)

Bien entendu, cela ne fonctionne que si les données sont effectivement représentées dans votre modèle de données. Ce n'est pas toujours le cas. Dans ces cas, vous pouvez envisager les options ci-dessous.

Balises et filtres personnalisés

La première solution est utile pour les requêtes qui ne sont que de pure présentation : balises personnalisées et filtres de modèles.

modèle.html

<h1>Welcome, {{ user|friendly_name }}</h1>

template_tags.py

@register.filter
def friendly_name(user):
    return remote_api.get_cached_name(user.id)

Méthodes d'interrogation

Si votre requête n'est pas purement formelle, vous pouvez ajouter des requêtes à votre site Web. services.py (si vous l'utilisez), ou introduisez un fichier queries.py module :

queries.py

def inactive_users():
    return User.objects.filter(active=False)

def users_called_publysher():
    for user in User.objects.all():
        if remote_api.get_cached_name(user.id) == "publysher":
            yield user 

Modèles de proxy

Les modèles proxy sont très utiles dans le contexte de la logique d'entreprise et du reporting. Vous définissez essentiellement un sous-ensemble amélioré de votre modèle. Vous pouvez remplacer l'ensemble de requêtes de base d'un gestionnaire en modifiant l'attribut Manager.get_queryset() méthode.

models.py

class InactiveUserManager(models.Manager):
    def get_queryset(self):
        query_set = super(InactiveUserManager, self).get_queryset()
        return query_set.filter(active=False)

class InactiveUser(User):
    """
    >>> for user in InactiveUser.objects.all():
    …        assert user.active is False 
    """

    objects = InactiveUserManager()
    class Meta:
        proxy = True

Modèles de requête

Pour les requêtes qui sont intrinsèquement complexes, mais qui sont exécutées assez souvent, il est possible d'utiliser des modèles de requêtes. Un modèle de requête est une forme de dénormalisation où les données pertinentes pour une seule requête sont stockées dans un modèle séparé. L'astuce consiste bien sûr à garder le modèle dénormalisé en synchronisation avec le modèle primaire. Les modèles de requête ne peuvent être utilisés que si les modifications sont entièrement sous votre contrôle.

models.py

class InactiveUserDistribution(models.Model):
    country = CharField(max_length=200)
    inactive_user_count = IntegerField(default=0)

La première option consiste à mettre à jour ces modèles dans vos commandes. C'est très utile si ces modèles ne sont modifiés que par une ou deux commandes.

forms.py

class ActivateUserForm(forms.Form):
    # see above

    def execute(self):
        # see above
        query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
        query_model.inactive_user_count -= 1
        query_model.save()

Une meilleure option serait d'utiliser des signaux personnalisés. Ces signaux sont bien sûr émis par vos commandes. Les signaux ont l'avantage de vous permettre de garder plusieurs modèles de requêtes en synchronisation avec votre modèle d'origine. De plus, le traitement des signaux peut être déchargé vers des tâches d'arrière-plan, en utilisant Celery ou des frameworks similaires.

signaux.py

user_activated = Signal(providing_args = ['user'])
user_deactivated = Signal(providing_args = ['user'])

forms.py

class ActivateUserForm(forms.Form):
    # see above

    def execute(self):
        # see above
        user_activated.send_robust(sender=self, user=user)

models.py

class InactiveUserDistribution(models.Model):
    # see above

@receiver(user_activated)
def on_user_activated(sender, **kwargs):
        user = kwargs['user']
        query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
        query_model.inactive_user_count -= 1
        query_model.save()

Maintenir la propreté

En utilisant cette approche, il devient ridiculement facile de déterminer si votre code reste propre. Il suffit de suivre ces directives :

  • Mon modèle contient-il des méthodes qui font plus que gérer l'état de la base de données ? Vous devriez extraire une commande.
  • Mon modèle contient-il des propriétés qui ne correspondent pas aux champs de la base de données ? Vous devez extraire une requête.
  • Mon modèle fait-il référence à une infrastructure qui n'est pas ma base de données (comme le courrier) ? Vous devriez extraire une commande.

Il en va de même pour les vues (car les vues souffrent souvent du même problème).

  • Ma vue gère-t-elle activement les modèles de base de données ? Vous devriez extraire une commande.

Quelques références

Documentation Django : modèles de proxy

Documentation Django : signaux

Architecture : Conception pilotée par le domaine

0 votes

Très perspicace ! Une question/problème : ces suggestions impliquent que tout le traitement se fasse à l'intérieur d'un site web django. Une architecture alternative consiste à utiliser django comme frontal web d'une couche de plateforme dorsale (cf. lethain.com/introduction-to-architecting-systems-for-scale/ ). Cette couche plate-forme permet d'exécuter du code en dehors du cadre du site Web. Le modèle de données, le modèle de domaine, la couche de service, etc. résideraient tous dans la couche plate-forme. Et, étant donné que cette couche se trouve en dehors de django, on pourrait utiliser SQL Alchemy comme ORM pour la couche plateforme au lieu de django.

17 votes

C'est agréable de voir une réponse intégrant la DDD dans une question relative à Django. Ce n'est pas parce que Django utilise ActiveRecord pour la persistance que la séparation des préoccupations doit disparaître. Excellente réponse.

0 votes

Où dois-je mettre services.py et comment accéder à ses méthodes ?

167voto

J'implémente généralement une couche de services entre les vues et les modèles. Celle-ci agit comme l'API de votre projet et vous donne une bonne vue d'ensemble de ce qui se passe. J'ai hérité cette pratique d'un de mes collègues qui utilise beaucoup cette technique de superposition avec des projets Java (JSF), par ex :

models.py

class Book:
   author = models.ForeignKey(User)
   title = models.CharField(max_length=125)

   class Meta:
       app_label = "library"

services.py

from library.models import Book

def get_books(limit=None, **filters):
    """ simple service function for retrieving books can be widely extended """
    return Book.objects.filter(**filters)[:limit]  # list[:None] will return the entire list

vues.py

from library.services import get_books

class BookListView(ListView):
    """ simple view, e.g. implement a _build and _apply filters function """
    queryset = get_books()

Remarquez, je prends généralement des modèles, des vues et des services séparer encore plus en fonction de la taille du projet.

9 votes

J'aime l'approche générale même si, d'après ce que j'ai compris, votre exemple spécifique serait généralement mis en œuvre sous la forme de manager .

9 votes

@arie pas nécessairement, peut-être qu'un meilleur exemple, pour une boutique en ligne, les services incluraient des choses comme la génération de sessions de panier, des tâches asynchrones comme le calcul des évaluations de produits, la création et l'envoi d'e-mails, etc.

4 votes

J'aime aussi cette approche, car je viens aussi de Java. Je suis novice en python, comment testeriez-vous views.py ? Comment simuler la couche service (si, par exemple, le service fait des appels api à distance) ?

75voto

dnozay Points 3672

Tout d'abord, Ne vous répétez pas .

Ensuite, veillez à ne pas vous surmener, car parfois, cela n'est qu'une perte de temps et fait perdre de vue l'essentiel. Passez en revue les zen de python de temps en temps.

Jetez un coup d'œil aux projets en cours

  • plus de personnes = plus de nécessité de s'organiser correctement
  • le site Dépôt django ils ont une structure simple.
  • le site Dépôt pip ils ont une structure de répertoire simple.
  • le site dépôt de tissus est également un bon point de vue.

    • vous pouvez placer tous vos modèles sous yourapp/models/logicalgroup.py
  • Par exemple User , Group et les modèles connexes peuvent passer sous yourapp/models/users.py
  • Par exemple Poll , Question , Answer ... pourrait s'effondrer yourapp/models/polls.py
  • chargez ce dont vous avez besoin dans __all__ à l'intérieur de yourapp/models/__init__.py

En savoir plus sur MVC

  • le modèle est vos données
    • cela inclut vos données réelles
    • ceci inclut également vos données de session / cookie / cache / fs / index
  • l'utilisateur interagit avec le contrôleur pour manipuler le modèle
    • il peut s'agir d'une API ou d'une vue qui enregistre ou met à jour vos données.
    • cela peut être réglé avec request.GET / request.POST ...etc.
    • pensez à radiomessagerie o filtrage aussi.
  • les données mettent à jour la vue
    • Les modèles prennent les données et les formatent en conséquence.
    • Les API, même sans modèle, font partie de la vue, par exemple tastypie o piston
    • cela devrait également tenir compte de l'intergiciel.

Profitez de intergiciel / balises de modèle

  • Si vous avez besoin qu'un travail soit effectué pour chaque demande, l'intergiciel est une solution.
    • par exemple, l'ajout d'horodatages
    • par exemple, la mise à jour des données sur les visites de pages
    • par exemple, alimenter un cache
  • Si vous avez des bouts de code qui reviennent toujours pour formater des objets, les balises de modèle sont utiles.
    • Par exemple, l'onglet actif / le fil d'Ariane de l'url.

Profitez de gestionnaires de modèles

  • créer User peut aller dans un UserManager(models.Manager) .
  • Les détails sanglants des instances doivent être publiés sur le models.Model .
  • détails gores pour queryset pourrait aller dans un models.Manager .
  • vous pourriez vouloir créer un User Vous pouvez donc penser qu'il devrait se trouver sur le modèle lui-même, mais lors de la création de l'objet, vous ne disposez probablement pas de tous les détails :

Exemple :

class UserManager(models.Manager):
   def create_user(self, username, ...):
      # plain create
   def create_superuser(self, username, ...):
      # may set is_superuser field.
   def activate(self, username):
      # may use save() and send_mail()
   def activate_in_bulk(self, queryset):
      # may use queryset.update() instead of save()
      # may use send_mass_mail() instead of send_mail()

Utilisez les formulaires lorsque cela est possible

Une grande partie du code passe-partout peut être éliminée si vous avez des formulaires qui correspondent à un modèle. Le site ModelForm documentation est assez bon. Séparer le code des formulaires du code du modèle peut être une bonne chose si vous avez beaucoup de personnalisation (ou parfois éviter les erreurs d'importation cycliques pour des utilisations plus avancées).

Utilice commandes de gestion quand c'est possible

  • par exemple yourapp/management/commands/createsuperuser.py
  • par exemple yourapp/management/commands/activateinbulk.py

si vous avez une logique d'entreprise, vous pouvez la séparer

  • django.contrib.auth utilise des backends tout comme db a un backend...etc.
  • ajouter un setting pour votre logique commerciale (par exemple AUTHENTICATION_BACKENDS )
  • vous pourriez utiliser django.contrib.auth.backends.RemoteUserBackend
  • vous pourriez utiliser yourapp.backends.remote_api.RemoteUserBackend
  • vous pourriez utiliser yourapp.backends.memcached.RemoteUserBackend
  • déléguer la logique commerciale difficile au backend
  • assurez-vous de bien définir l'attente sur l'entrée/sortie.
  • changer la logique métier est aussi simple que de changer un paramètre :)

exemple de backend :

class User(db.Models):
    def get_present_name(self): 
        # property became not deterministic in terms of database
        # data is taken from another service by api
        return remote_api.request_user_name(self.uid) or 'Anonymous' 

pourrait devenir :

class User(db.Models):
   def get_present_name(self):
      for backend in get_backends():
         try:
            return backend.get_present_name(self)
         except: # make pylint happy.
            pass
      return None

en savoir plus sur les modèles de conception

en savoir plus sur les limites de l'interface

  • Le code que vous voulez utiliser fait-il vraiment partie des modèles ? -> yourapp.models
  • Le code fait-il partie de la logique métier ? -> yourapp.vendor
  • Le code fait-il partie des outils génériques / libs ? -> yourapp.libs
  • Le code fait-il partie des librairies de logique commerciale ? -> yourapp.libs.vendor o yourapp.vendor.libs
  • En voici une bonne : pouvez-vous tester votre code de manière indépendante ?
    • oui, bien :)
    • non, vous avez peut-être un problème d'interface
    • quand il y a une séparation claire, unittest devrait être un jeu d'enfant avec l'utilisation de la moquerie
  • La séparation est-elle logique ?
    • oui, bien :)
    • non, vous pouvez avoir des difficultés à tester ces concepts logiques séparément.
  • Pensez-vous que vous aurez besoin de refactoriser lorsque vous aurez 10x plus de code ?
    • oui, no good, no bueno, refactor pourrait être beaucoup de travail
    • Non, c'est juste génial !

En bref, vous auriez pu

  • yourapp/core/backends.py
  • yourapp/core/models/__init__.py
  • yourapp/core/models/users.py
  • yourapp/core/models/questions.py
  • yourapp/core/backends.py
  • yourapp/core/forms.py
  • yourapp/core/handlers.py
  • yourapp/core/management/commands/__init__.py
  • yourapp/core/management/commands/closepolls.py
  • yourapp/core/management/commands/removeduplicates.py
  • yourapp/core/middleware.py
  • yourapp/core/signals.py
  • yourapp/core/templatetags/__init__.py
  • yourapp/core/templatetags/polls_extras.py
  • yourapp/core/views/__init__.py
  • yourapp/core/views/users.py
  • yourapp/core/views/questions.py
  • yourapp/core/signals.py
  • yourapp/lib/utils.py
  • yourapp/lib/textanalysis.py
  • yourapp/lib/ratings.py
  • yourapp/vendor/backends.py
  • yourapp/vendor/morebusinesslogic.py
  • yourapp/vendor/handlers.py
  • yourapp/vendor/middleware.py
  • yourapp/vendor/signals.py
  • yourapp/tests/test_polls.py
  • yourapp/tests/test_questions.py
  • yourapp/tests/test_duplicates.py
  • yourapp/tests/test_ratings.py

ou tout ce qui peut vous aider ; trouver le interfaces dont vous avez besoin et le limites vous aidera.

29voto

Chris Pratt Points 53859

Django utilise un type de MVC légèrement modifié. Il n'y a pas de concept de "contrôleur" dans Django. Le substitut le plus proche est une "vue", ce qui tend à créer une confusion avec les convertisseurs MVC, car dans MVC une vue ressemble plus au "modèle" de Django.

Dans Django, un "modèle" n'est pas simplement une abstraction de base de données. À certains égards, il partage avec la "vue" de Django le rôle de contrôleur du MVC. Il contient l'intégralité du comportement associé à une instance. Si cette instance doit interagir avec une API externe dans le cadre de son comportement, il s'agit toujours de code de modèle. En fait, les modèles ne sont pas du tout tenus d'interagir avec la base de données, de sorte que vous pourriez concevoir des modèles qui existent entièrement en tant que couche interactive d'une API externe. C'est un concept de "modèle" beaucoup plus libre.

0 votes

Pour ceux qui sont intéressés, il y a un FAQ sur MVC dans la documentation de Django.

9voto

Nacho Gentile Points 166

Dans Django, la structure MVC est, comme l'a dit Chris Pratt, différente du modèle MVC classique utilisé dans d'autres frameworks. Je pense que la raison principale de cette différence est d'éviter une structure d'application trop stricte, comme cela se produit dans d'autres frameworks MVC comme CakePHP.

Dans Django, MVC a été implémenté de la manière suivante :

La couche de visualisation est divisée en deux. Les vues ne doivent être utilisées que pour gérer les requêtes HTTP, elles sont appelées et y répondent. Les vues communiquent avec le reste de votre application (formulaires, modelforms, classes personnalisées, ou dans des cas simples directement avec les modèles). Pour créer l'interface, nous utilisons des Templates. Les modèles sont semblables à des chaînes de caractères pour Django, il leur attribue un contexte, et ce contexte est communiqué à la vue par l'application (lorsque la vue le demande).

La couche modèle permet l'encapsulation, l'abstraction, la validation, l'intelligence et rend vos données orientées objet (on dit qu'un jour les SGBD le feront aussi). Cela ne signifie pas que vous devez créer d'énormes fichiers models.py (en fait, un très bon conseil est de diviser vos modèles en différents fichiers, de les placer dans un dossier appelé 'models', de créer un fichier '__init__.py' dans ce dossier où vous importez tous vos modèles et enfin d'utiliser l'attribut 'app_label' de la classe models.Model). Le modèle doit vous abstraire de l'utilisation des données, il rendra votre application plus simple. Vous pouvez également, si nécessaire, créer des classes externes, comme des "outils" pour vos modèles. Vous pouvez également utiliser l'héritage dans les modèles, en définissant l'attribut "abstract" de la classe Meta de votre modèle à "True".

Où est le reste ? Eh bien, les petites applications web sont généralement une sorte d'interface avec les données. Dans certains cas de petits programmes, l'utilisation de vues pour interroger ou insérer des données serait suffisante. Dans des cas plus courants, on utilisera des Forms ou des ModelForms, qui sont en fait des "contrôleurs". Ce n'est pas autre chose qu'une solution pratique à un problème commun, et une solution très rapide. C'est ce qu'un site web doit faire.

Si les formulaires ne sont pas suffisants pour vous, alors vous devriez créer vos propres classes pour faire la magie, un très bon exemple de ceci est l'application d'administration : vous pouvez lire le code de ModelAmin, qui fonctionne en fait comme un contrôleur. Il n'y a pas de structure standard, je vous suggère d'examiner les applications Django existantes, cela dépend de chaque cas. C'est ce que les développeurs de Django ont prévu, vous pouvez ajouter une classe d'analyseur xml, une classe de connecteur API, ajouter Celery pour effectuer des tâches, tordre pour une application basée sur reactor, utiliser seulement l'ORM, faire un service web, modifier l'application d'administration et plus encore... C'est votre responsabilité de faire du code de bonne qualité, de respecter ou non la philosophie MVC, de le rendre basé sur des modules et de créer vos propres couches d'abstraction. C'est très flexible.

Mon conseil : lisez autant de code que vous le pouvez, il y a beaucoup d'applications django autour de vous, mais ne les prenez pas au sérieux. Chaque cas est différent, les modèles et la théorie aident, mais pas toujours, c'est une science imprécise, django vous fournit juste de bons outils que vous pouvez utiliser pour soulager certaines douleurs (comme l'interface d'administration, la validation des formulaires web, l'i18n, l'implémentation du modèle d'observation, tout ce qui a été mentionné précédemment et d'autres), mais les bonnes conceptions viennent de concepteurs expérimentés.

PS : utilisez la classe 'User' de l'application auth (de django standard), vous pouvez faire par exemple des profils d'utilisateurs, ou au moins lire son code, il sera utile pour votre cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X