Il existe probablement d'autres solutions. Dans cet article je vous donne les clés pour réussir à trier correctement les utilisateurs par nom et prénom. L'exercice est assez simple lorsque l'utilisateur a renseigné correctement son identité, c'est à dire "Nom et Prénom". Cela devient plus chaotique, quand un seul des champs est renseigné.
Liste factice
- LEGRAND Simon - simon@mail.com
- THOMAS Robert - robert@mail.com
- PETIT Catherine - catherine@mail.com
- BONNET Georges - georges@mail.com
- THOMAS Alfred - alfred@mail.com
Alfred et Robert ne se connaissent pas
Solution de fortune
User.objects.filter(...).order_by('last_name', 'first_name')
Soyons honnête cette requête suffit dans la majorité des cas. Mais que fait elle ? Elle trie par ordre alphabétique par nom puis prénom. Donc appliquée sur la liste ci-dessus cela nous donne :
- BONNET Georges - georges@mail.com
- LEGRAND Simon - simon@mail.com
- PETIT Catherine - catherine@mail.com
- THOMAS Alfred - alfred@mail.com
- THOMAS Robert - robert@mail.com
Maintenant imaginons le pire. Mr LEGRAND Simon et Mme Catherine ne renseignent plus leur nom de famille. On obtient ainsi :
- Simon - simon@mail.com
- Catherine - catherine@mail.com
- BONNET Georges - georges@mail.com
- THOMAS Alfred - alfred@mail.com
- THOMAS Robert - robert@mail.com
Oui mais moi je veux voir Simon et Catherine à leur place c'est à dire en se basant sur le "S" de Simon et le "C" de Catherine.
Trois solutions pour trier la liste
Trois solutions, un seule retenue. Il n'y a pas de meilleure, tout dépend de votre besoin.
- Utiliser Raw Sql pour fusionner la colonne "last_name" et "first_name" et effectuer un tri sur cette fusion
- Créer un nouveau champs dans la DB, celui ci étant la fusion de "last_name" et de "first_name"
- Dénormaliser la requête et utiliser une méthode métier
- Première solution : Performance moyenne et obligation d'écrire en SQL. Requête simple si cela provient directement du models "User", beaucoup plus compliqué si c'est une liste d'utilisateur extraite d'un projet. (project.get_users) .. avec potentiellement une multitude de filtres.
- Deuxième solution : Performance haute, obligation de créer un nouvel attribut dans User Profile, surcharger la méthode save(). Si votre base est en place, vous devez générer cette colonne. Oblige également à passer par get_profile(). Le plus pratique serait de directement modifier la table "User".
- Troisième solution: Performance moyenne, vous devez ajouter une méthode au models User (User redéfini par Meta Class) et écrire la dénormalisation.
J'ai choisi la troisième solution, l'historique du projet facilitait le choix. En effet User était déjà redéfini par MetaClass donc finalement une méthode, une dénormalisation et c'était dans la poche.
La méthode métier sur User
Nommez-la comme vous voulez.
def get_full_name_inverse(self): return (("%s %s") % (self.last_name.capitalize(), self.first_name.capitalize())).strip()
- Cette méthode fusionne le nom et le prénom. Elle supprime les espace en début et fin indésirable. Très important pour le tri !
- Cette méthode ajoute une belle majuscule au nom et au prénom. L'intérêt est uniquement visuel
La dénormalisation
users = User.objects.filter(...) users = sorted(users, key=lambda a: a.get_full_name_inverse())
Et voici la version qui permet de gérer les caractères accentués.
import locale users = User.objects.filter(...) locale.setlocale(locale.LC_ALL, "fr_FR.UTF-8") users = sorted(users, cmp=locale.strcoll, key=lambda a: a.get_full_name_inverse())
On obtient ainsi la liste suivante :
- BONNET Georges - georges@mail.com
- Catherine - catherine@mail.com
- Simon - simon@mail.com
- THOMAS Alfred - alfred@mail.com
- THOMAS Robert - robert@mail.com