#27111: django.contrib.auth.forms.UserCreationForm.__init__ method expects
USERNAME_FIELD to be in self.fields
-------------------------+-------------------------------------------------
     Reporter:           |      Owner:  nobody
  petercampbell          |
         Type:  Bug      |     Status:  new
    Component:  Forms    |    Version:  1.10
     Severity:  Normal   |   Keywords:  custom user, django admin,
 Triage Stage:           |  UserCreationForm
  Unreviewed             |  Has patch:  0
Easy pickings:  0        |      UI/UX:  0
-------------------------+-------------------------------------------------
 Between the latest 1.9.x release and 1.10 release the following method was
 added to django.contrib.auth.forms.UserCreationForm lines 95-97

     {{{#!python
 def __init__(self, *args, **kwargs):
         super(UserCreationForm, self).__init__(*args, **kwargs)
 self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus':
 ''})
 }}}

 which sets the username field to autofocus when the form is rendered.

 This is quite probably a very specific use case but in my current project
 I am subclassing from AbstractUser to create my own custom user:
 {{{#!python
     class User(AbstractBaseUser, PermissionsMixin):
         """
         A custom user
         """
         uuid = models.UUIDField(
             _('UUID'),
             max_length=36,
             unique=True,
             help_text=_('The unique identifier used to login to the
 system.')
         )
         ...
         USERNAME_FIELD = 'uuid'
 }}}
 Note that this project uses a uuid field as its username field. This
 project will only enable internal and external systems to connect to it
 via an API (DRF) so authentication will be token based using the username
 and associated password as the entry point into the system. As such I do
 not want django usernames to be created manually, instead creating them
 automatically as the user object is saved. The exact method of user
 creation may vary but for this particular issue I am focusing on
 subclassing the UserCreationForm:
 {{{#!python
     import uuid

     from django.contrib import admin
     from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
     from django.contrib.auth.forms import UserCreationForm as
 DjangoUserCreationForm
     from django.utils.translation import ugettext_lazy as _

     from .models import User


     class UserCreationForm(DjangoUserCreationForm):
         """
         Custom user creation form enables the password to be set but
 handles the uuid creation internally on save.
         """
         class Meta(DjangoUserCreationForm.Meta):
             model = User
             fields = ('description',)

         def save(self, commit=True):
             user = super().save(commit=False)
             user.uuid = uuid.uuid4()
             user.set_password(self.cleaned_data["password1"])
             if commit:
                 user.save()
             return user


     class UserAdmin(DjangoUserAdmin):
         add_form = UserCreationForm
         fieldsets = (
             (None, {'fields': ['password', 'description']}),
             (_('Permissions'), {'fields': ('is_active', 'is_staff',
 'is_superuser',
                                            'groups',
 'user_permissions')}),
             (_('Important dates'), {'fields': ('last_login',
 'date_joined')}),
         )
         add_fieldsets = (
             (
                 None, {
                     'classes': ('wide',),
                     'fields': ('password1', 'password2')
                 }
             ),
         )
         list_display = ('uuid', 'description', 'is_active', 'is_staff',
 'is_superuser')
         search_fields = ('uuid',)
         ordering = ('uuid',)

     admin.site.register(User, UserAdmin)
 }}}
 Note in my subclassed UserCreationForm.save method I set the username
 value to a newly created uuid and in my subclassed UserAdmin class the
 only fields available for creation are password1 and password2,
 effectively disabling username input.

 Going back to the __init__ method above and in this specific case in the
 admin console, attempting to load the user create form results in the
 USERNAME_FIELD being inaccessible (in fact the fields attribute is also
 unavailable at this point), resulting in the following error:

 ''KeyError at /admin/users/user/add/''

 //'uuid'//

 which points directly to this line
 (django.contrib.auth.forms.UserCreationForm.__init__ line - 97):
 {{{#!python
 self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus':
 ''})
 }}}
 To work around this in my subclassed UserCreationForm I have added:
 {{{#!python
     def __init__(self, *args, **kwargs):
         if hasattr(self, 'fields') and self._meta.model.USERNAME_FIELD in
 self.fields:
 self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus':
 ''})

         super(DjangoUserCreationForm, self).__init__(*args, **kwargs)
 }}}
 Note that in order to ensure the __init__ method of the parent of
 django.contrib.auth.forms.UserCreationForm
 (django.forms.models.BaseModelForm) is called I call the super
 implementation directly, which probably isn't wise but in this case works
 for me - I cannot call the immediate parent as it will result in the same
 error.

 I understand my logic might be edge case but I feel this is bug, hence why
 it is reported as such.

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

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/056.02e8d1cafb8fc5af7655de86088a8493%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to