Chainer des Mixins d'un même type

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour ou bonsoir tout le monde,

D'avance, je m'excuse pour le titre de mon sujet. Il m'est difficile de décrire clairement et simplement la question que je vous pose aujourd'hui. Ceci étant dit …

Dans le cadre du développement technique de Zeste de Savoir, j'espère pouvoir développer prochainement le ping d'un utilisateur (voire d'un groupe). La syntaxe serait la suivante : @**Pseudo** (si vous n'aimez pas cette syntaxe, je vous redirige vers le sujet de la ZEP qui s'occupe de spécifier la chose) et pourrait figurer à plusieurs reprises dans un message d'un sujet, une conversation privée ou même d'un contenu. Jusque là, pas trop de questions.

Maintenant, parlons un peu implémentation. J'ai un peu réfléchi à la question et j'ai une solution. Reste à savoir si elle est dans l'esprit du développement Django, voire même python. Quant j'ai pensé ma solution, j'avais deux objectifs en tête :

  • Être générique sur l'opération faite : Aujourd'hui, on parse une chaîne de caractères pour récupérer tous les pings. Demain, il faut un mécanisme pour rajouter facilement d'autres opérations en touchant le moins possible l'implémentation de l'application de l'opération.
  • Eviter la répétition de code. Cela ne me semble pas nécessaire de développer ce point d'avantage. Vous pouvez aisément comprendre où se situe la répétition de code si nous applications le parse sur des contenus différents à plusieurs endroits.

Sachant ceci, voici ce que j'ai imaginé :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Vue très simplifiée pour mettre en évidence la mixin et l'invocation autour du texte du message.
class PostNew(CreatePostView, PingOperationMixin):
    object = None

    def form_valid(self, form):
        # self.operation vient de la mixin PingOperationMixin.
        text = self.operation(form.data.get('text')
        topic = send_post(self.request, self.object, self.request.user, text), send_by_mail=True)
        return redirect(topic.last_message.get_absolute_url())

    def get_object(self, queryset=None):
        try:
            topic_pk = int(self.request.GET.get('sujet'))
        except (KeyError, ValueError, TypeError):
            raise Http404
        return get_object_or_404(Topic, pk=topic_pk)
1
2
3
class PingOperationMixin(OperationMixin):
    def operation(self, text):
        # do stuff.
1
2
3
class OperationMixin(objects):
    def operation(self, text):
        raise NotImplementedError

Selon moi, cela permet :

  1. D'avoir toute la logique du ping dans une classe dédiée.
  2. D'avoir un faible impact sur le code où j'applique l'opération du ping.
  3. Chainer les OperationMixin dans l'entête de la classe cible. (Si j'ai bien compris python, self.operation() sera appelé pour chaque mixin déclaré).

Qu'est ce que vous en pensez ? Trouvez-vous que c'est dans l'esprit Django/Python ? Je ne fais pas d'erreur avec mon point 3 sur le chainage des OperationMixin ?

Merci d'avance de vos réponses !

+0 -0
Staff

Cette réponse a aidé l'auteur du sujet

Être générique sur l'opération faite : Aujourd'hui, on parse une chaîne de caractères pour récupérer tous les pings. Demain, il faut un mécanisme pour rajouter facilement d'autres opérations en touchant le moins possible l'implémentation de l'application de l'opération.

il faut donc utiliser un registry et non un mixin (comme pour les templatetags). Car changer la signature pour ajouter une opération "générique" c'est pas vraiment cool ni évolutif.

Quitte à utiliser l'interface, autant utiliser une simple boucle qui itère sur une liste de callable. Je préfère que tu aies un simple "operation mixin" qui fait ceci:

1
2
3
4
5
6
class TextOperationMixin(objects):
    operation_registry = None
    def operation(self, text):
        if self.operation_registry and is_instance(self.operation_registry, list):
            for operation in self.operation_registry:
                text = operation(text)

ensuite il n'y aurait plus qu'à créer une fonction do_ping dans un fichier style ton_package.utils.py pour dans l'action mettre ceci :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class PostNew(CreatePostView, OperationMixin):
    object = None
    operation_registry = [do_ping]
    def form_valid(self, form):
        # self.operation vient de la mixin PingOperationMixin.
        text = self.operation(form.data.get('text')
        topic = send_post(self.request, self.object, self.request.user, text), send_by_mail=True)
        return redirect(topic.last_message.get_absolute_url())

    def get_object(self, queryset=None):
        try:
            topic_pk = int(self.request.GET.get('sujet'))
        except (KeyError, ValueError, TypeError):
            raise Http404
        return get_object_or_404(Topic, pk=topic_pk)

Édité par artragis

+0 -0
Auteur du sujet

Hormis le fait que je vais surment faire un assert plutôt qu'une condition et que je vais tenter d'éviter un fichier utils, c'est vrai que ta solution est pas mal et pourrait mieux convenir à ma situation.

Merci artragis !

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte