On 11/22/2015 11:56 AM, Aymeric Augustin wrote:
> I spent a good part of today implementing what must be the most common
> scenario for custom user models: case-insensitive email as username.
> (Yes. This horse has been beaten to death. Multiple times.)
> Since it was the first time I implemented a custom user model from
> scratch by myself, I’d like to share my experience in case that’s useful
> to others. Do you think there’s a better solution? Do you have concrete
> ideas for improving Django in this area?
> The main alternative I’m aware of is a custom email field based on
> PostgreSQL’s citext type. Perhaps I’ll try that next time. Anyway,
> here’s what I did this time.
I've implemented the CITEXT-based solution a couple times; I think for a
PostgreSQL-based project it's the preferable option overall.
The main complexity is just in adding a dependency on a Postgres
extension, which is unfortunately non-trivial in general -- you can't
easily just stick "CREATE EXTENSION" in a migration since creating the
extension requires database super-user privileges, which the Django db
user may not have. I usually just punt by documenting that the project
requires you to set up you database with the extension installed.
I suppose the best we could do to ease this would be to add a
CreateExtension migration operation in contrib.postgres that, if lacking
super-user permissions, simply errors out and tells you what SQL you
need to run manually as a super-user?
I also had to add a connection_created handler to register a conversion
to unicode for CITEXT fields, as psycopg2 otherwise returned them as str
(this was Python 2 - that issue may not exist in Python 3). This issue
would be easy for Django to fix if we want to bake in any level of
CITEXT support in contrib.postgres.
Once you clear those initial hurdles, the CITEXT solution is smooth
sailing. All the rest of the issues covered in your post go away
entirely, since the database handles all the case-management (original
case preserved but ignored in lookups and indexes) internally, and the
ORM doesn't need to know anything about it.