diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index 74b0f2ba8ee..5ffbc8207c2 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -11,9 +11,9 @@ class UserCreationForm(forms.ModelForm): """ A form that creates a user, with no privileges, from the given username and password. """ - username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^\w+$', - help_text = _("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."), - error_message = _("This value must contain only letters, numbers and underscores.")) + username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^[\w.@+-]+$', + help_text = _("Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."), + error_message = _("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.")) @@ -45,9 +45,9 @@ class UserCreationForm(forms.ModelForm): return user class UserChangeForm(forms.ModelForm): - username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^\w+$', - help_text = _("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."), - error_message = _("This value must contain only letters, numbers and underscores.")) + username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^[\w.@+-]+$', + help_text = _("Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."), + error_message = _("This value may contain only letters, numbers and @/./+/-/_ characters.")) class Meta: model = User diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 3bfd98270da..0be88754122 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -177,7 +177,7 @@ class User(models.Model): Username and password are required. Other fields are optional. """ - username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores).")) + username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters")) first_name = models.CharField(_('first name'), max_length=30, blank=True) last_name = models.CharField(_('last name'), max_length=30, blank=True) email = models.EmailField(_('e-mail address'), blank=True) diff --git a/django/contrib/auth/tests/forms.py b/django/contrib/auth/tests/forms.py index 482979c59ee..a6ab2c448bf 100644 --- a/django/contrib/auth/tests/forms.py +++ b/django/contrib/auth/tests/forms.py @@ -21,7 +21,7 @@ False # The username contains invalid data. >>> data = { -... 'username': 'jsmith@example.com', +... 'username': 'jsmith!', ... 'password1': 'test123', ... 'password2': 'test123', ... } @@ -29,7 +29,7 @@ False >>> form.is_valid() False >>> form["username"].errors -[u'This value must contain only letters, numbers and underscores.'] +[u'This value may contain only letters, numbers and @/./+/-/_ characters.'] # The verification password is incorrect. @@ -65,7 +65,7 @@ False # The success case. >>> data = { -... 'username': 'jsmith2', +... 'username': 'jsmith2@example.com', ... 'password1': 'test123', ... 'password2': 'test123', ... } @@ -73,7 +73,7 @@ False >>> form.is_valid() True >>> form.save() - + # The user submits an invalid username. @@ -189,7 +189,7 @@ True >>> form.is_valid() False >>> form['username'].errors -[u'This value must contain only letters, numbers and underscores.'] +[u'This value may contain only letters, numbers and @/./+/-/_ characters.'] ### PasswordResetForm diff --git a/docs/releases/1.2.txt b/docs/releases/1.2.txt index 703ded34378..38df3b120c3 100644 --- a/docs/releases/1.2.txt +++ b/docs/releases/1.2.txt @@ -742,3 +742,10 @@ views in your :ref:`URLconf `. This means that you can maintain complete control over the URL structure of your feeds. Like any other view, feeds views are passed a ``request`` object, so you can do anything you would normally do with a view, like user based access control, or making a feed a named URL. + +Relaxed requirements for usernames +---------------------------------- + +The built-in :class:`~django.contrib.auth.models.User` model's +:attr:`~django.contrib.auth.models.User.username` field now allows a wider range +of characters, including ``@``, ``+``, ``.`` and ``-`` characters. diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index 1c52ab674e9..0ff2ee6f5ee 100644 --- a/docs/topics/auth.txt +++ b/docs/topics/auth.txt @@ -71,6 +71,9 @@ Fields Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores). + + .. versionchanged:: 1.2 + Usernames may now contain ``@``, ``+``, ``.`` and ``-`` characters. .. attribute:: models.User.first_name