Validation no been applied on user creation
patillacode opened this issue · 4 comments
Hi,
I have managed to make django-passwords for default django admin users, but only when they try to change their password. I don't seem to find a way to make this work when creating a user .
Here is my code:
forms.py
from django.contrib.auth.forms import SetPasswordForm,PasswordChangeForm
from django.utils.translation import ugettext_lazy as _
from passwords.fields import PasswordField
class ValidatingSetPasswordForm(SetPasswordForm):
new_password2 = PasswordField(label=_("New password confirmation"))
class ValidatingPasswordChangeForm(PasswordChangeForm):
new_password2 = PasswordField(label=_("New password confirmation"))
As you can see I have been able to override the field new_password2 setting it up as a PasswordField
Then I force those urls to go through my forms, see below:
urls.py
urlpatterns = patterns('',
url(r'^admin/password_change/$', 'django.contrib.auth.views.password_change',{'password_change_form': ValidatingPasswordChangeForm}),
url(r'^admin/password_changed/$', 'django.contrib.auth.views.password_change_done'),
url(r'^admin/password_reset/$', 'django.contrib.auth.views.password_reset'),
url(r'^admin/password_reset_done/$', 'django.contrib.auth.views.password_reset_done'),
url(r'^admin/password_reset_complete/$', 'django.contrib.auth.views.password_reset_complete'),
url(r'^admin/password_reset_confirm/(?P<uidb36>[-\w]+)/(?P<token>[-\w]+)/$','django.contrib.auth.views.password_reset_confirm',{'set_password_form': ValidatingSetPasswordForm}),
url(r'^admin/', include(admin.site.urls)),
)
What am I missing?
Any help would be highly appreciated.
Hm, I'm not sure, but is it possible that on the SetPasswordForm the field isn't called new_password2
but actually something like only password2
?
@maccesch
Hi, thanks for the quick response.
Is not that, I have just tried. I have also tried to figure out the URL that I think is missing.
Let me explain.
When I create a user I go to localhost:9000/admin/auth/user/add/
But that URL is not in my urls.py file (see first comment) - so, ok, lets try to put it in there.
Doing the same thing I have done up to now, it would be something like:
url(r'^admin/auth/user/add/$', 'django.contrib.auth.views.I_DONT_KNOW_WHAT_TO_PUT_HERE',{'password_change_form': ValidatingSetPasswordForm}),
The problem comes when I don't know what is the actual view I need to put there :(
I have been looking through django files and I found this:
contrib/auth/forms.py
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and
password.
"""
error_messages = {
'duplicate_username': _("A user with that username already exists."),
'password_mismatch': _("The two password fields didn't match."),
}
username = forms.RegexField(label=_("Username"), max_length=30,
regex=r'^[\w.@+-]+$',
help_text = _("Required. 30 characters or fewer. Letters, digits and "
"@/./+/-/_ only."),
error_messages = {
'invalid': _("This value may contain only letters, numbers and "
"@/./+/-/_ characters.")})
password1 = forms.CharField(label=_("Password"),
widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password confirmation"),
widget=forms.PasswordInput,
help_text = _("Enter the same password as above, for verification."))
class Meta:
model = User
fields = ("username",)
def clean_username(self):
# Since User.username is unique, this check is redundant,
# but it sets a nicer error message than the ORM. See #13147.
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate_username'])
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'])
return password2
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
That is the form you would see when going to that url, so:
1 - yes, it is called password2 (thanks for that)
2 - the main problem is to find the view that calls that form into place.
Digging a little deeper I found this:
contrib/auth/admin.py
class UserAdmin(admin.ModelAdmin):
add_form_template = 'admin/auth/user/add_form.html'
change_user_password_template = None
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'password1', 'password2')}
),
)
form = UserChangeForm
add_form = UserCreationForm
change_password_form = AdminPasswordChangeForm
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
list_filter = ('is_staff', 'is_superuser', 'is_active')
search_fields = ('username', 'first_name', 'last_name', 'email')
ordering = ('username',)
filter_horizontal = ('user_permissions',)
def get_fieldsets(self, request, obj=None):
if not obj:
return self.add_fieldsets
return super(UserAdmin, self).get_fieldsets(request, obj)
def get_form(self, request, obj=None, **kwargs):
"""
Use special form during user creation
"""
defaults = {}
if obj is None:
defaults.update({
'form': self.add_form,
'fields': admin.util.flatten_fieldsets(self.add_fieldsets),
})
defaults.update(kwargs)
return super(UserAdmin, self).get_form(request, obj, **defaults)
def get_urls(self):
from django.conf.urls import patterns
return patterns('',
(r'^(\d+)/password/$',
self.admin_site.admin_view(self.user_change_password))
) + super(UserAdmin, self).get_urls()
There you can see the get_form method does a dynamic form, so, I still don't know where the actual view is being called in order to get that form in the admin UI. Or the urls.py file that handles it.
Any other thoughts?
PS: sorry for the long post with lots of code
I guess you could try to use a custom User
ModelAdmin
and then overwrite the field add_form
there.
http://stackoverflow.com/a/2552554/298941
Totally, and is what I have thought about since I don't find any other way but I wanted to have a better solution, more elegant, like what I have showed you. Is more subtle if you may.
Also, it has become personal, why can I do this on a change password form but not on the create user one? :P
I will try to figure it out or just fallback into overriding the whole model.
Cheers.