[Django] #32897: create_user() does not raise ValidationError

22 views
Skip to first unread message

Django

unread,
Jul 2, 2021, 7:15:06 AM7/2/21
to django-...@googlegroups.com
#32897: create_user() does not raise ValidationError
----------------------------------------+------------------------
Reporter: adamckay | Owner: nobody
Type: Bug | Status: new
Component: contrib.auth | Version: 3.2
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
----------------------------------------+------------------------
The `django.contrib.auth.models.UserManager.create_user()` function does
not perform validation checking of the user object prior to saving to the
database.

The documentation
([https://docs.djangoproject.com/en/3.2/ref/contrib/auth/#django.contrib.auth.models.UserManager.create_user])
implies this is the canonical way to create a user object and indeed it
performs functions to normalize the email address and username as well as
setting an unusable password, however the object is not validated using
`full_clean()` which results in an object that has validation errors
attempting to be committed to the database raising an `IntegrityError`
rather than a `ValidationError`.

This impacts the ability to add errors to any forms which use
`create_user()` as the `IntegrityError` does not return a user-friendly
message, nor is it applied to a field element.

Reproduction steps in a Django shell:

{{{
>>> from django.contrib.auth.models import User
>>> User.objects.create_user('example')
<User: example>
>>> User.objects.create_user('example')
Traceback (most recent call last):
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/backends/sqlite3/base.py", line 423, in execute
return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: UNIQUE constraint failed: auth_user.username

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/contrib/auth/models.py", line 152, in create_user
return self._create_user(username, email, password, **extra_fields)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/contrib/auth/models.py", line 146, in _create_user
user.save(using=self._db)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/contrib/auth/base_user.py", line 67, in save
super().save(*args, **kwargs)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/models/base.py", line 726, in save
self.save_base(using=using, force_insert=force_insert,
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/models/base.py", line 763, in save_base
updated = self._save_table(
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/models/base.py", line 868, in _save_table
results = self._do_insert(cls._base_manager, using, fields,
returning_fields, raw)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/models/base.py", line 906, in _do_insert
return manager._insert(
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/models/query.py", line 1270, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/models/sql/compiler.py", line 1416, in execute_sql
cursor.execute(sql, params)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/backends/utils.py", line 98, in execute
return super().execute(sql, params)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/backends/utils.py", line 66, in execute
return self._execute_with_wrappers(sql, params, many=False,
executor=self._execute)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/.pyenv/versions/38-django-core/lib/python3.8/site-
packages/django/db/backends/sqlite3/base.py", line 423, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed:
auth_user.username

}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/32897>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Jul 2, 2021, 10:14:41 AM7/2/21
to django-...@googlegroups.com
#32897: create_user() does not raise ValidationError
------------------------------+--------------------------------------
Reporter: Adam McKay | Owner: nobody
Type: Bug | Status: closed
Component: contrib.auth | Version: 3.2
Severity: Normal | Resolution: invalid

Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------
Changes (by Tim Graham):

* status: new => closed
* resolution: => invalid


Comment:

The lack of validation is consistent with `Model.save()`, etc. I see
nothing in the documentation that suggests this would behave otherwise.

--
Ticket URL: <https://code.djangoproject.com/ticket/32897#comment:1>

Django

unread,
Jul 2, 2021, 10:39:47 AM7/2/21
to django-...@googlegroups.com
#32897: create_user() does not raise ValidationError
------------------------------+--------------------------------------
Reporter: Adam McKay | Owner: nobody
Type: Bug | Status: closed
Component: contrib.auth | Version: 3.2
Severity: Normal | Resolution: invalid

Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by Adam McKay):

Replying to [comment:1 Tim Graham]:


> The lack of validation is consistent with `Model.save()`, etc. I see
nothing in the documentation that suggests this would behave otherwise.

Hi Tim,

I agree the behaviour is consistent with `Model.save()`, however I think
the usage of `create_user` goes beyond a typical model method as it will
ensure a `username` is specified (raises an exception if not), normalises
`username` and `email` and can also generate a `password` if one is not
specified.

Perhaps I should have logged this as a feature enhancement to call
`full_clean()` prior to `user.save()` rather than a bug? My intention
being to get a more accurate error from the method.

--
Ticket URL: <https://code.djangoproject.com/ticket/32897#comment:2>

Django

unread,
Jul 2, 2021, 2:17:04 PM7/2/21
to django-...@googlegroups.com
#32897: create_user() does not raise ValidationError
------------------------------+--------------------------------------
Reporter: Adam McKay | Owner: nobody
Type: Bug | Status: closed
Component: contrib.auth | Version: 3.2
Severity: Normal | Resolution: invalid

Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by Tim Graham):

I doubt there would be consensus to change the behavior, particularly
because it could be backward incompatible, but you could try making your
case on the DevelopersMailingList. The requirements of your use case
aren't entirely clear, but an alternative could be to use
`django.contrib.auth.forms.UserCreationForm`.

--
Ticket URL: <https://code.djangoproject.com/ticket/32897#comment:3>

Reply all
Reply to author
Forward
0 new messages