załóżmy, że mamy model w django zdefiniowany w następujący sposób:
class Literal:
name = models.CharField(...)
...
Pole nazwy nie jest unikalne i dlatego może mieć zduplikowane wartości. Muszę wykonać następujące zadania: Wybierz wszystkie wiersze z modelu, które mają co najmniej jeden duplikat wartości w name
polu.
Wiem jak to zrobić używając zwykłego SQL (może nie być najlepszym rozwiązaniem):
select * from literal where name IN (
select name from literal group by name having count((name)) > 1
);
Czy jest więc możliwe wybranie tego za pomocą django ORM? Albo lepsze rozwiązanie SQL?
sql
django
django-orm
dragon
źródło
źródło
Literal.objects.values('name').annotate(name_count=Count('name')).filter(name_count__gt=1)
?Cannot resolve keyword 'id_count' into field
values_list('name', flat=True)
Count
adnotacji do zapisania jako, domyślnie jest to[field]__count
. Jednak składnia podwójnego podkreślenia jest również tym, jak Django interpretuje, że chcesz wykonać złączenie. Tak więc, zasadniczo, kiedy próbujesz to filtrować, Django myśli, że próbujesz zrobić połączenie,count
które oczywiście nie istnieje. Rozwiązaniem jest określenie nazwy wyniku adnotacji,annotate(mycount=Count('id'))
a następnie włączenie filtrumycount
.values('name')
po wywołaniu adnotacji, możesz usunąć rozumienie listy i powiedzieć,Literal.objects.filter(name__in=dupes)
które pozwoli to wszystko wykonać w jednym zapytaniu.Zostało to odrzucone jako zmiana. Więc tutaj jest to lepsza odpowiedź
Zwróci to
ValuesQuerySet
ze wszystkimi zduplikowanymi nazwami. Możesz jednak użyć tego do skonstruowania zwykłegoQuerySet
, przesyłając go z powrotem do innego zapytania. ORM django jest wystarczająco inteligentny, aby połączyć je w jedno zapytanie:Dodatkowe wywołanie
.values('name')
po wywołaniu adnotacji wygląda trochę dziwnie. Bez tego podzapytanie kończy się niepowodzeniem. Dodatkowe wartości sprawiają, że ORM wybiera tylko kolumnę nazwy dla podzapytania.źródło
.order_by()
?GROUP BY
klauzuli SQL , a to psuje. Odkryłem to, grając z Subquery (w którym robisz bardzo podobne grupowanie przez.values()
)spróbuj użyć agregacji
źródło
Jeśli używasz PostgreSQL, możesz zrobić coś takiego:
Wynika z tego raczej proste zapytanie SQL:
SELECT unnest(ARRAY_AGG("app_literal"."id")) AS "ids" FROM "app_literal" GROUP BY "app_literal"."name" HAVING array_length(ARRAY_AGG("app_literal"."id"), 1) > 1
źródło
Jeśli chcesz uzyskać tylko listę nazw, ale nie obiekty, możesz użyć następującego zapytania
źródło