﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
27985	Converting `Foo.objects.filter(bar=None)` to an `IsNull` too early.	Jarek Glowacki	Sergey Fedoseev	"This is a little similar to #25946, but different angle.

Case Study:

We define a custom Field which converts emptystring to `None` before sending to DB and then `None` back to emptystring when reading from DB:

WHY (optional reading):
  For want of a CharField that enforces uniqueness on non-empty elements only. `'' == ''` in the DB, but `NULL != NULL`. We don't want to just allow passing None into it in the code though. Hence NulledCharField.

Example Implementation:
{{{
class NulledCharField(models.CharField):
    description = _(""CharField that stores NULL for emptystrings but returns ''."")

    def from_db_value(self, value, expression, connection, context):
        if value is None:
            return ''
        else:
            return value

    def to_python(self, value):
        if isinstance(value, models.CharField):
            return value
        if value is None:
            return ''
        return value

    def get_prep_value(self, value):
        if value is '':
            return None
        else:
            return value
}}}

This works for the most part, but not with filtering!
I took a look at the SQL, and `Foo.objects.filter(bar='')` resolves to `... WHERE ""Foo"".""bar"" = NULL; args=(None,)`
Compare this to `Foo.objects.filter(bar=None)`, which resolves to `... WHERE ""Foo"".""bar"" IS NULL', ()`

The reason for this is that the `=None` -> `__isnull=True` conversion happens before any transformation of the value.

Specifically, in django/db/models/sql/query.py,
`Query.build_filter()` calls on `self.prepare_lookup_value()` (line1139) before it calls `self.build_lookup()` (line 1187).
The former does the isnull replacement, while the latter transforms the value.

This also applies (more severely) to the converse: If my `get_prep_value()` were to for some reason convert `None` to something else, then this would get ignored as Django would just decide to use the `IsNull` lookup off the bat.

Solution seems like we just need to rearrange the order here a little. Happy to give it a go, but if anyone has context as to whether it's maybe this way by design, do tell. The above cases would at the very least serve as good failing tests if we think this kind of custom field support is appropriate.

"	Bug	closed	Database layer (models, ORM)	dev	Normal	fixed	sql None NULL transform	Simon Charette	Accepted	1	0	0	0	0	0
