J'essaie de construire la recherche d'un site Django que je suis en train de construire, et dans cette recherche, je cherche dans 3 modèles différents. Et pour obtenir la pagination sur la liste des résultats de recherche, je voudrais utiliser une vue générique object_list pour afficher les résultats. Mais pour ce faire, je dois fusionner 3 ensembles de requêtes en un seul.
Comment puis je faire ça? J'ai essayé ça:
result_list = []
page_list = Page.objects.filter(
Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term))
article_list = Article.objects.filter(
Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term) |
Q(tags__icontains=cleaned_search_term))
post_list = Post.objects.filter(
Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term) |
Q(tags__icontains=cleaned_search_term))
for x in page_list:
result_list.append(x)
for x in article_list:
result_list.append(x)
for x in post_list:
result_list.append(x)
return object_list(
request,
queryset=result_list,
template_object_name='result',
paginate_by=10,
extra_context={
'search_term': search_term},
template_name="search/result_list.html")
Mais ça ne marche pas. J'obtiens une erreur lorsque j'essaie d'utiliser cette liste dans la vue générique. La liste ne contient pas l'attribut clone.
Est-ce que quelqu'un sait comment je peux fusionner les trois listes page_list
, article_list
et post_list
?
django
search
django-queryset
django-q
espenhogbakk
la source
la source
union
.Réponses:
La concaténation des ensembles de requêtes dans une liste est l'approche la plus simple. Si la base de données est de toute façon atteinte pour tous les ensembles de requêtes (par exemple parce que le résultat doit être trié), cela n'augmentera pas le coût.
L'utilisation
itertools.chain
est plus rapide que la mise en boucle de chaque liste et l'ajout d'éléments un par un, car elleitertools
est implémentée en C. Elle consomme également moins de mémoire que la conversion de chaque ensemble de requêtes en liste avant la concaténation.Il est maintenant possible de trier la liste résultante par exemple par date (comme demandé dans le commentaire de hasen j à une autre réponse). La
sorted()
fonction accepte facilement un générateur et retourne une liste:Si vous utilisez Python 2.4 ou version ultérieure, vous pouvez utiliser à la
attrgetter
place d'un lambda. Je me souviens avoir lu que c'était plus rapide, mais je n'ai pas vu de différence de vitesse notable pour un million d'articles.la source
from itertools import groupby
unique_results = [rows.next() for (key, rows) in groupby(result_list, key=lambda obj: obj.id)]
'list' object has no attribute 'complex_filter'
Essaye ça:
Il conserve toutes les fonctions des ensembles de requêtes, ce qui est bien si vous le souhaitez
order_by
ou similaire.Remarque: cela ne fonctionne pas sur les ensembles de requêtes de deux modèles différents.
la source
|
l'opérateur d'union défini, pas OR au niveau du bit.Associé, pour mélanger des ensembles de requêtes du même modèle, ou pour des champs similaires de quelques modèles, à partir de Django 1.11, une
qs.union()
méthode est également disponible:https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.union
la source
Vous pouvez utiliser la
QuerySetChain
classe ci-dessous. Lorsque vous l'utilisez avec le paginateur de Django, il ne devrait frapper la base de données qu'avec desCOUNT(*)
requêtes pour tous les ensembles deSELECT()
requêtes et des requêtes uniquement pour les ensembles de requêtes dont les enregistrements sont affichés sur la page actuelle.Notez que vous devez spécifier
template_name=
si vous utilisez unQuerySetChain
avec des vues génériques, même si les ensembles de requêtes chaînés utilisent tous le même modèle.Dans votre exemple, l'utilisation serait:
Ensuite, utilisez
matches
avec le paginateur comme vous l'avez utiliséresult_list
dans votre exemple.Le
itertools
module a été introduit dans Python 2.3, il devrait donc être disponible dans toutes les versions Python sur lesquelles Django fonctionne.la source
Le gros inconvénient de votre approche actuelle est son inefficacité avec de grands ensembles de résultats de recherche, car vous devez à chaque fois retirer l'ensemble de résultats de la base de données, même si vous ne souhaitez afficher qu'une page de résultats.
Pour extraire uniquement les objets dont vous avez réellement besoin de la base de données, vous devez utiliser la pagination sur un QuerySet, pas une liste. Si vous faites cela, Django coupe réellement le QuerySet avant que la requête ne soit exécutée, donc la requête SQL utilisera OFFSET et LIMIT pour obtenir uniquement les enregistrements que vous afficherez réellement. Mais vous ne pouvez pas le faire à moins que vous ne puissiez recadrer votre recherche en une seule requête.
Étant donné que les trois de vos modèles ont des champs de titre et de corps, pourquoi ne pas utiliser l' héritage de modèle ? Faites hériter les trois modèles d'un ancêtre commun qui a un titre et un corps, et effectuez la recherche en une seule requête sur le modèle ancêtre.
la source
Si vous souhaitez chaîner un grand nombre d'ensembles de requêtes, essayez ceci:
où: docs est une liste d'ensembles de requêtes
la source
Cité sur https://groups.google.com/forum/#!topic/django-users/6wUNuJa4jVw . Voir Alex Gaynor
la source
Cela peut être réalisé de deux manières.
1ère façon de le faire
Utilisez l'opérateur union pour queryset
|
pour prendre l'union de deux queryset. Si les deux ensembles de requêtes appartiennent au même modèle / modèle unique, il est possible de combiner les ensembles de requêtes à l'aide de l'opérateur d'union.Pour une instance
2ème façon de le faire
Une autre façon de réaliser une opération de combinaison entre deux ensembles de requêtes consiste à utiliser la fonction de chaîne itertools .
la source
Exigences:
Django==2.0.2
,django-querysetsequence==0.8
Dans le cas où vous souhaitez combiner
querysets
et toujours sortir avec unQuerySet
, vous voudrez peut-être vérifier django-queryset-sequence .Mais une note à ce sujet. Il n'en faut que deux
querysets
comme argument. Mais avec python,reduce
vous pouvez toujours l'appliquer à plusieursqueryset
s.Et c'est tout. Voici une situation que j'ai rencontrée et comment j'ai travaillé
list comprehension
,reduce
etdjango-queryset-sequence
la source
Book.objects.filter(owner__mentor=mentor)
pas la même chose? Je ne suis pas sûr que ce soit un cas d'utilisation valide. Je pense qu'unBook
peut avoir besoin d'avoir plusieursowner
s avant de commencer à faire quelque chose comme ça.voici une idée ... il suffit de tirer vers le bas une page complète des résultats de chacun des trois, puis de jeter les 20 moins utiles ... cela élimine les grands ensembles de requêtes et de cette façon vous ne sacrifiez qu'un peu de performances au lieu de beaucoup
la source
Cela fera le travail sans utiliser d'autres bibliothèques
la source
Cette fonction récursive concatène un tableau d'ensembles de requêtes en un seul ensemble de requêtes.
la source