2017-09-14 3 views
0

Un élément de code contient un certain QuerySet de DB, puis ajoute un nouveau champ calculé à chaque objet de l'ensemble de requêtes. Ce n'est pas une option pour ajouter ce champ via l'annotation (parce que c'est un héritage et parce que ce calcul est basé sur d'autres données déjà pré-extraites).Réorganiser le QuerySet Django par champ ajouté dynamiquement

Comme ceci:

from django.db import models 
class Human(models.Model): 
    name = models.CharField() 
    surname = models.CharField() 

def calculate_new_field(s): 
    return len(s.name)*42 

people = Human.objects.filter(id__in=[1,2,3,4,5]) 

for s in people: 
    s.new_column = calculate_new_field(s) 
# people.somehow_reorder(new_order_by=new_column) 

Alors maintenant, tous les QuerySet ont une nouvelle colonne. Et je veux commander ces objets par le champ new_column. order_by() ne fonctionnera pas évidemment, puisqu'il s'agit d'une option de base de données. Je comprends que je peux les passer comme une liste triée, mais il y a beaucoup de modèles et d'autres logiques, qui attendent de cet objet l'interface de QuerySet-like avec ses méthodes et ainsi de suite. Donc la question est: existe-t-il une façon pas très mauvaise et sale de réordonner un QuerySet existant par un champ ajouté de manière dynamique ou de créer un nouvel objet QuerySet avec ces données? Je crois que je ne suis pas le seul qui a fait face à ce problème et il est déjà résolu avec django. Mais je ne trouve rien (sauf pour ajouter des bibliothèques tierces, et ce n'est pas une option aussi).

Répondre

0

Pouvez-vous l'essayer functions.Length:

from django.db.models.functions import Length 

qs = Human.objects.filter(id__in=[1,2,3,4,5]) 
qs.annotate(reorder=Length('name') * 42).order_by('reorder') 
+0

En fait, je ne commande pas les colonnes par longueur, c'est un pourcentage calculé. J'ai simplement utilisé la longueur pour affirmer que la colonne ajoutée est basiquement un nombre calculé d'une manière ou d'une autre. Désolé si je vous ai manqué. – Paul

+0

et comme vous pouvez le voir, vous pouvez ajouter un calcul pour annoter et l'utiliser pour l'ordre, essayez d'afficher votre code réel –

0

Conceptuellement, le QuerySet n'est pas une liste de résultats, mais les « instructions pour obtenir ces résultats ». Il est évalué paresseusement et également mis en cache. L'attribut interne du QuerySet qui conserve les résultats mis en cache est qs._result_cache

Ainsi, la phrase for s in people force l'évaluation de la requête et la mise en cache des résultats.

Vous pouvez, après cela, trier les résultats en faisant:

people._result_cache.sort(key=attrgetter('new_column'))

Mais, après avoir évalué un QuerySet, il fait peu de sens (à mon avis) pour garder l'interface QuerySet, comme beaucoup d'entre les opérations provoqueront une réévaluation de la requête. À partir de ce point, vous devriez avoir affaire à une liste de modèles