Як зробити нерівну фільтрацію набору запитів Джанго?


664

У моделі Django QuerySets я бачу, що є __gtі __ltдля порівняльних значень, але чи є __ne/ !=/ <>( не дорівнює ?)

Я хочу відфільтрувати за допомогою не рівного:

Приклад:

Model:
    bool a;
    int x;

мені потрібно

results = Model.objects.exclude(a=true, x!=5)

!=Чи не правильний синтаксис. Я спробував __ne, <>.

Я закінчив використовувати:

results = Model.objects.exclude(a=true, x__lt=5).exclude(a=true, x__gt=5)

75
Чи спрацювали б результати = Model.objects.exclude (a = true) .filter (x = 5)?
hughdbrown

3
@hughdbrown. Ні. Ваш запит виключає всі a=trueспочатку, а потім застосовує x=5фільтр до решти. Запропонований запит вимагав лише тих, хто має a=trueта x!=5. Різниця полягала в тому, що всі, хто має a=trueі x=5також відфільтрований.
Мітчелл ван Зуйлен

Відповіді:


689

Можливо, об'єкти Q можуть допомогти для цієї проблеми. Я ніколи їх не використовував, але, здається, їх можна заперечувати та поєднувати так само, як звичайні вирази пітона.

Оновлення: Я щойно спробував це, схоже, він працює досить добре:

>>> from myapp.models import Entry
>>> from django.db.models import Q

>>> Entry.objects.filter(~Q(id = 3))

[<Entry: Entry object>, <Entry: Entry object>, <Entry: Entry object>, ...]

16
@ JCLeitão: див. Також відповідь @ d4nt нижче для більш інтуїтивного синтаксису.
Пол Д. Уейт

610

Здається, у вашого запиту є подвійний мінус, ви хочете виключити всі рядки, де х не 5, тому іншими словами ви хочете включити всі рядки, де х IS 5. Я вважаю, що це зробить трюк.

results = Model.objects.filter(x=5).exclude(a=true)

Щоб відповісти на ваше конкретне запитання, немає "не рівного", але це, мабуть, тому, що у django є і "фільтрувати", і "виключати" методи, тому ви завжди можете просто перемикати логічний раунд, щоб отримати бажаний результат.


2
@ d4nt: Можливо, я помиляюся, але я думаю, що запит повинен бутиresults = Model.objects.filter(a=true).exclude(x=5)
Taranjeet

1
@Taranjeet: Я думаю, ви неправильно прочитали оригінальний запит. Версія d4nt правильна, тому що ОП хотів виключити (a = True) і заперечити виключення x = 5 (тобто включити його).
Чак

3
Я думаю, що це неправильно, оскільки екземпляр (x = 4, a = false) був би неправильно виключений.
RemcoGerlich

4
@danigosa Це не здається правильним. Я просто спробував це сам, і порядок , excludeі filterдзвінки не робити будь - яких значимих розходжень. Порядок умов у WHEREпункті змінюється, але як це має значення?
coredumperror

4
@danigosa порядок виключення та фільтрування не має значення.
EralpB

132

field=valueсинтаксис запитів є узагальнюючим для field__exact=value. Тобто Django ставить операторів запитів на поля запитів у своїх ідентифікаторах . Django підтримує таких операторів:

exact
iexact
contains
icontains
in
gt
gte
lt
lte
startswith
istartswith
endswith
iendswith
range
year
month
day
week_day
isnull
search
regex
iregex

Я впевнений, поєднуючи їх із Q-об’єктами, як пропонує Дейв Фогт, і використовуючи, filter()або exclude()як Джейсон Бейкер пропонує, ви отримаєте саме те, що вам потрібно для будь-якого можливого запиту.


спасибі це надзвичайно. я використав щось подібне, tg=Tag.objects.filter(user=request.user).exclude(name__regex=r'^(public|url)$')і це працює.
suhailvs

@suhail, зауважте, що не всі бази даних підтримують синтаксис регулярного
виразів

2
i in icontains, iexactі подібні означає "ігнорування чутливості до регістру". Це не для "обернених".
Плющ росте

Варто зазначити, що при використанні exclude()декількох термінів, можливо, ви захочете скласти пропозицію з ORоператором, наприклад exclude(Q(field1__queryop1=value1) | Q(field2__queryop2=value2)), щоб виключити результати за обох умов.
клапас

98

Створити спеціальний пошук за допомогою Django 1.7 легко. У офіційній документації Django є __neприклад пошуку .

Спочатку потрібно створити сам пошук:

from django.db.models import Lookup

class NotEqual(Lookup):
    lookup_name = 'ne'

    def as_sql(self, qn, connection):
        lhs, lhs_params = self.process_lhs(qn, connection)
        rhs, rhs_params = self.process_rhs(qn, connection)
        params = lhs_params + rhs_params
        return '%s <> %s' % (lhs, rhs), params

Тоді вам потрібно зареєструвати його:

from django.db.models.fields import Field
Field.register_lookup(NotEqual)

Тепер ви можете використовувати __neпошук у своїх запитах так:

results = Model.objects.exclude(a=True, x__ne=5)

88

У Django 1.9 / 1.10 є три варіанти.

  1. Ланцюг excludeіfilter

    results = Model.objects.exclude(a=true).filter(x=5)
  2. Використовуйте Q()об'єкти та ~оператора

    from django.db.models import Q
    object_list = QuerySet.filter(~Q(a=True), x=5)
  3. Зареєструйте спеціальну функцію пошуку

    from django.db.models import Lookup
    from django.db.models.fields import Field
    
    @Field.register_lookup
    class NotEqual(Lookup):
        lookup_name = 'ne'
    
        def as_sql(self, compiler, connection):
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params
            return '%s <> %s' % (lhs, rhs), params

    register_lookupДекоратор був доданий в Django 1.8 і дозволяє призначений для користувача LookUp як зазвичай:

    results = Model.objects.exclude(a=True, x__ne=5)

1
object_list = QuerySet.filter (~ Q (a = Істинно), x = 5): Не забудьте зберегти всі інші умови, що не містять Q, після тих, що містять Q.
Bhumi Singhal

1
@MichaelHoffmann: A) Ви будете фільтрувати на менший набір даних після виключення, використовуючи ~ Q, тим ефективніше. Б) напевно, послідовність навпаки не працює .. не знаю .. не пам’ятай!
Bhumi Singhal

41

У той час як з моделями, ви можете фільтрувати з =, __gt, __gte, __lt, __lte, ви не можете використовувати ne, !=або <>. Однак ви можете досягти кращої фільтрації за допомогою об’єкта Q.

Ви можете уникнути зчеплення QuerySet.filter()і QuerySet.exlude(), і використовувати це:

from django.db.models import Q
object_list = QuerySet.filter(~Q(field='not wanted'), field='wanted')

24

В очікуванні проектного рішення. Тим часом використовуйтеexclude()

У трекері випуску Django є чудовий запис № 5763 під назвою "Набір запитів не має" не рівний "оператор фільтру" . Він чудовий тим, що (станом на квітень 2016 року) його "відкрили 9 років тому" (у кам'яному віці Джанго), "закрили 4 роки тому" та "востаннє змінили 5 місяців тому".

Прочитайте обговорення, це цікаво. В принципі, деякі люди стверджують , __neповинні бути додані в той час як інші говорять , що exclude()ясніше і , отже , __ne повинен НЕ бути додані.

(Я погоджуюся з першим, тому що останній аргумент приблизно рівносильний тому, що говорити, що Python не повинен мати, !=тому що він є ==і notвже ...)


22

Використання виключення та фільтрування

results = Model.objects.filter(x=5).exclude(a=true)


8

Останній біт коду буде виключати всі об'єкти, де x! = 5 і a - True. Спробуйте це:

results = Model.objects.filter(a=False, x=5)

Пам'ятайте, що знак = у наведеному вище рядку присвоює параметр False параметру a, а число 5 - параметру x. Це не перевірка рівності. Таким чином, насправді не існує жодного способу використання символу! = У виклику запиту.


3
Це не на 100% те саме, оскільки для цих полів також можуть бути нульові значення.
MikeN

Це повертається лише в тих елементах, які мають a = False і x = 5, але у питанні екземпляр (a = false, x = 4) буде включений.
RemcoGerlich

1
results = Model.objects.filter(a__in=[False,None],x=5)
Джеремі

8

Результати = Model.objects.filter (a = True) .exclude (x = 5)
Генерає цей sql:
виберіть * з таблиціx, де a! = 0 і x! = 5
Sql залежить від того, як представлено ваше поле True / False та механізму бази даних. Код джанго - це все, що вам потрібно.



6

Що ви шукаєте - це всі об'єкти, які мають a=false або x=5 . У Django |служить ORоператором між наборами запитів:

results = Model.objects.filter(a=false)|Model.objects.filter(x=5)

5

Це дасть бажаний результат.

from django.db.models import Q
results = Model.objects.exclude(Q(a=True) & ~Q(x=5))

для рівних ви можете використовувати ~за рівним запитом. очевидно, Qможе бути використаний для досягнення рівного запиту.


Перевірте редагування; використання "і" в Q(a=True) and ~Q(x=5)оцінюється ~Q(x=5)як аргументи .exclude. Будь ласка, прочитайте: docs.python.org/3/reference/expressions.html#boolean-operations та docs.python.org/3/reference/… .
tzot

2

Слідкуйте за безліччю неправильних відповідей на це питання!

Логіка Джерарда правильна, хоча вона поверне список, а не набір запитів (що може не мати значення).

Якщо вам потрібен набір запитів, використовуйте Q:

from django.db.models import Q
results = Model.objects.filter(Q(a=false) | Q(x=5))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.