Próbuję zbudować wyszukiwanie dla witryny Django, którą tworzę, i podczas tego wyszukiwania szukam w 3 różnych modelach. Aby uzyskać paginację na liście wyników wyszukiwania, chciałbym użyć ogólnego widoku lista_obiektu do wyświetlenia wyników. Ale żeby to zrobić, muszę scalić 3 zestawy zapytań w jeden.
Jak mogę to zrobić? Próbowałem tego:
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")
Ale to nie działa. Podczas próby użycia tej listy w widoku ogólnym pojawia się błąd. Na liście brakuje atrybutu klonowania.
Czy ktoś wie, jak mogę połączyć trzy listy page_list
, article_list
i post_list
?
django
search
django-queryset
django-q
espenhogbakk
źródło
źródło
union
.Odpowiedzi:
Łączenie zestawów zapytań w listę jest najprostszym podejściem. Jeśli baza danych i tak zostanie trafiona dla wszystkich zestawów zapytań (np. Ponieważ wynik musi zostać posortowany), nie spowoduje to dodatkowych kosztów.
Używanie
itertools.chain
jest szybsze niż zapętlanie każdej listy i dodawanie elementów jeden po drugim, ponieważitertools
jest zaimplementowane w C. Zużywa również mniej pamięci niż przekształcanie każdego zestawu zapytań w listę przed konkatenacją.Teraz można posortować wynikową listę, np. Według daty (zgodnie z żądaniem w komentarzu hasen j do innej odpowiedzi).
sorted()
Funkcja korzystnie przyjmuje generator i zwraca listy:Jeśli używasz
attrgetter
języka Python 2.4 lub nowszego, możesz użyć zamiast lambda. Pamiętam, że czytałem o tym, że jest szybszy, ale nie zauważyłem zauważalnej różnicy prędkości na liście milionów przedmiotów.źródło
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'
Spróbuj tego:
Zachowuje wszystkie funkcje zestawów zapytań, co jest miłe, jeśli chcesz
order_by
lub podobnie.Uwaga: to nie działa na zestawach zapytań z dwóch różnych modeli.
źródło
|
operator ustawionego związku, nie bitowy LUB.Powiązane, do mieszania zestawów zapytań z tego samego modelu lub do podobnych pól z kilku modeli, począwszy od Django 1.11
qs.union()
metoda dostępna jest również:https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.union
źródło
Możesz skorzystać z
QuerySetChain
poniższej klasy. Używając go z paginatorem Django, powinien on trafić do bazy danych zCOUNT(*)
zapytaniami dla wszystkich zestawówSELECT()
zapytań i zapytaniami tylko dla tych zestawów zapytań, których rekordy są wyświetlane na bieżącej stronie.Pamiętaj, że musisz określić,
template_name=
czy używaszQuerySetChain
widoków ogólnych, nawet jeśli wszystkie powiązane zestawy zapytań używają tego samego modelu.W twoim przykładzie użycie to:
Następnie użyj
matches
z paginatorem, tak jakresult_list
w przykładzie.itertools
Moduł został wprowadzony w Pythonie 2.3, więc powinien on być dostępny we wszystkich wersjach Pythona Django biegnie dalej.źródło
Dużym minusem obecnego podejścia jest jego nieskuteczność w przypadku dużych zestawów wyników wyszukiwania, ponieważ za każdym razem trzeba wyciągać cały zestaw wyników z bazy danych, nawet jeśli zamierza się wyświetlić tylko jedną stronę wyników.
Aby wyciągnąć tylko te obiekty, których naprawdę potrzebujesz z bazy danych, musisz użyć paginacji na QuerySet, a nie na liście. Jeśli to zrobisz, Django faktycznie wycina QuerySet przed wykonaniem zapytania, więc zapytanie SQL użyje OFFSET i LIMIT, aby uzyskać tylko rekordy, które faktycznie wyświetlisz. Ale nie możesz tego zrobić, chyba że w jakiś sposób możesz wcisnąć swoje wyszukiwanie w jedno zapytanie.
Biorąc pod uwagę, że wszystkie trzy modele mają pola tytułu i treści, dlaczego nie zastosować dziedziczenia modelu ? Wystarczy, że wszystkie trzy modele odziedziczą po wspólnym przodku, który ma tytuł i treść, i wykonaj wyszukiwanie jako pojedyncze zapytanie w modelu przodka.
źródło
Jeśli chcesz połączyć wiele zestawów zapytań, spróbuj tego:
gdzie: docs to lista zestawów zapytań
źródło
Cytat z https://groups.google.com/forum/#!topic/django-users/6wUNuJa4jVw . Zobacz Alex Gaynor
źródło
Można to osiągnąć na dwa sposoby.
Pierwszy sposób to zrobić
Użyj operatora unii dla zestawu zapytań,
|
aby uzyskać połączenie dwóch zestawów zapytań. Jeśli oba zestawy zapytań należą do tego samego modelu / jednego modelu, możliwe jest połączenie zestawów zapytań za pomocą operatora unii.Na przykład
Drugi sposób to zrobić
Innym sposobem osiągnięcia operacji łączenia między dwoma zestawami zapytań jest użycie funkcji łańcucha itertools .
źródło
Wymagania:
Django==2.0.2
,django-querysetsequence==0.8
Jeśli chcesz połączyć
querysets
i nadal wychodzić zQuerySet
, możesz chcieć sprawdzić sekwencję django-queryset .Ale jedna uwaga na ten temat.
querysets
Argument wymaga tylko dwóch . Ale w Pythoniereduce
zawsze możesz zastosować go do wieluqueryset
s.I to wszystko. Poniżej znajduje się sytuacja, na którą natknąłem się i jak to zrobiłem
list comprehension
,reduce
orazdjango-queryset-sequence
źródło
Book.objects.filter(owner__mentor=mentor)
nie robi tego samego? Nie jestem pewien, czy jest to prawidłowy przypadek użycia. Myślę, żeBook
może być konieczne podanie wieluowner
s, zanim zaczniesz robić coś takiego.oto pomysł ... po prostu ściągnij jedną pełną stronę wyników z każdego z trzech, a następnie wyrzuć 20 najmniej przydatnych ... to eliminuje duże zestawy zapytań i w ten sposób poświęcasz tylko niewielką wydajność zamiast dużo
źródło
Spowoduje to wykonanie pracy bez użycia żadnych innych bibliotek
źródło
Ta funkcja rekurencyjna łączy tablicę zestawów zapytań w jeden zestaw zapytań.
źródło