{{{
print(M.objects.exclude(closed_at__year=t.year).values("closed_at__year").query)
SELECT
EXTRACT('year' FROM "r_m"."closed_at" AT TIME ZONE 'Europe/London')
FROM "r_m"
WHERE NOT (
"r_m"."closed_at" BETWEEN 2021-01-01 00:00:00+00:00 AND 2021-12-31
23:59:59.999999+00:00
AND "r_m"."closed_at" IS NOT NULL
)
}}}
{{{
print(M.objects.annotate(year=ExtractYear("closed_at")).exclude(year=t.year).values("year").query)
SELECT
EXTRACT('year' FROM "r_m"."closed_at" AT TIME ZONE 'Europe/London')
AS "year"
FROM "r_m"
WHERE NOT (
"r_m"."closed_at" BETWEEN 2021-01-01 00:00:00+00:00 AND 2021-12-31
23:59:59.999999+00:00
)
}}}
Annotated aggregates like min and max also don't account for nulls which
leads me to suspect this is a problem with annotations.
I also tried wrapping the ExtractYear in an ExpressionWrapper with
`output_field=IntegerField(null=True)` which didn't change anything.
--
Ticket URL: <https://code.djangoproject.com/ticket/32398>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* cc: Jacob Walls (added)
* type: Uncategorized => Bug
* component: Uncategorized => Database layer (models, ORM)
* stage: Unreviewed => Accepted
Comment:
Thanks for the report. In this case a `WHERE` clause is
[https://github.com/django/django/blob/6822aa5c6c3fbec7c5393a05e990865ba59fe167/django/db/models/sql/query.py#L1302-L1305
returned] before
[https://github.com/django/django/blob/6822aa5c6c3fbec7c5393a05e990865ba59fe167/django/db/models/sql/query.py#L1354-L1375
handling nullable columns].
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:1>
Comment (by Gordon Wrigley):
Whoever looks at it might want to take a quick look at the other early
returns in the same function.
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:2>
* cc: Simon Charette (added)
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:3>
* owner: nobody => Jordan Bae
* status: new => assigned
Comment:
Could i look into this and fix?
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:4>
Comment (by Jacob Walls):
No problem (no need to ask).
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:5>
* needs_better_patch: 0 => 1
* has_patch: 0 => 1
Comment:
[https://github.com/django/django/pull/14065 PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:6>
* needs_better_patch: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:7>
* needs_better_patch: 0 => 1
* needs_tests: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:8>
* has_patch: 1 => 0
* needs_tests: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:9>
* needs_better_patch: 1 => 0
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:10>
Comment (by Jordan Bae):
if anyone has time, please check this
PR(https://github.com/django/django/pull/14065)
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:11>
Comment (by Mariusz Felisiak):
#32684 was a duplicate for a subquery annotation.
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:12>
* needs_better_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:13>
Comment (by Gerben Morsink):
I think I have found another case that fails and is related to handling
nullable columns:
{{{
value = ['1', None]
status_model =
StatusModel.objects.filter(field=OuterRef('pk')).order_by('-timestamp')
qs =
RelatedModel.annotate(current_status_db=Subquery(status_model.values('status')[:1])).filter(current_status_db__in=value)
}}}
qs always returns an empty queryset, while if the filter equals
{{{.filter(current_status_db=None)}}} it works correctly.
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:14>
* owner: Jordan Bae => Jacob Walls
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:15>
* needs_better_patch: 1 => 0
Comment:
[https://github.com/django/django/pull/16748 preliminary PR to add
is_nullable()]
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:16>
* needs_better_patch: 0 => 1
Comment:
Per Simon's
[https://github.com/django/django/pull/16748#issuecomment-1502162075
comment].
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:17>
* owner: Jacob Walls => Simon Charette
Comment:
Resetting assignee as mentioned
[https://github.com/django/django/pull/16748#issuecomment-1564001712
here].
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:18>
Comment (by Natalia Bidart):
#34959 was also closed as duplicate of this one.
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:19>
Comment (by Roman Odaisky):
Or sometimes the null handling is applied in places where it should not be
applied:
{{{
def isblank(value, true_or_false):
"""Basically the equivalent of `(not value) == true_or_false`"""
return Exact(Exact(value, 0) | IsNull(value, True), true_or_false)
def lookupify(function):
class LookupifyLookup(Lookup):
prepare_rhs = False
def as_sql(self, compiler, connection):
return compiler.compile(
function(self.lhs, self.rhs)
.resolve_expression(compiler.query)
)
return LookupifyLookup
Field.register_lookup(lookupify(isblank), "isblank")
SomeModel.objects.exclude(isblank(F("field"), True))
# correctly excludes zeroes and nulls
SomeModel.objects.exclude(field__isblank=True)
# only excludes zeroes but not nulls!
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/32398#comment:20>