diff -rupN auth/admin.py /home/kyle/django/contrib/auth/admin.py --- auth/admin.py 2009-01-10 11:29:58.000000000 -0700 +++ /home/kyle/django/contrib/auth/admin.py 2009-01-08 22:05:40.000000000 -0700 @@ -17,8 +17,8 @@ class GroupAdmin(admin.ModelAdmin): class UserAdmin(admin.ModelAdmin): fieldsets = ( - (None, {'fields': ('username', 'password')}), - (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}), + (None, {'fields': ('email', 'password')}), + (_('Personal info'), {'fields': ('first_name', 'last_name')}), (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}), (_('Important dates'), {'fields': ('last_login', 'date_joined')}), (_('Groups'), {'fields': ('groups',)}), @@ -26,10 +26,10 @@ class UserAdmin(admin.ModelAdmin): form = UserChangeForm add_form = UserCreationForm change_password_form = AdminPasswordChangeForm - list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff') + list_display = ('email', 'first_name', 'last_name', 'is_staff') list_filter = ('is_staff', 'is_superuser') - search_fields = ('username', 'first_name', 'last_name', 'email') - ordering = ('username',) + search_fields = ('email', 'first_name', 'last_name') + ordering = ('email',) filter_horizontal = ('user_permissions',) def __call__(self, request, url): @@ -74,7 +74,7 @@ class UserAdmin(admin.ModelAdmin): 'auto_populated_fields': (), 'opts': self.model._meta, 'save_as': False, - 'username_help_text': self.model._meta.get_field('username').help_text, + 'email_help_text': self.model._meta.get_field('email').help_text, 'root_path': self.admin_site.root_path, 'app_label': self.model._meta.app_label, }, context_instance=template.RequestContext(request)) @@ -93,7 +93,7 @@ class UserAdmin(admin.ModelAdmin): else: form = self.change_password_form(user) return render_to_response('admin/auth/user/change_password.html', { - 'title': _('Change password: %s') % escape(user.username), + 'title': _('Change password: %s') % escape(user.email), 'form': form, 'is_popup': '_popup' in request.REQUEST, 'add': True, diff -rupN auth/backends.py /home/kyle/django/contrib/auth/backends.py --- auth/backends.py 2009-01-10 11:29:58.000000000 -0700 +++ /home/kyle/django/contrib/auth/backends.py 2009-01-08 22:05:40.000000000 -0700 @@ -13,9 +13,9 @@ class ModelBackend(object): """ # TODO: Model, login attribute name and password attribute name should be # configurable. - def authenticate(self, username=None, password=None): + def authenticate(self, email=None, password=None): try: - user = User.objects.get(username=username) + user = User.objects.get(email=email) if user.check_password(password): return user except User.DoesNotExist: diff -rupN auth/forms.py /home/kyle/django/contrib/auth/forms.py --- auth/forms.py 2009-01-10 11:29:58.000000000 -0700 +++ /home/kyle/django/contrib/auth/forms.py 2009-01-08 22:05:40.000000000 -0700 @@ -9,25 +9,25 @@ from django.utils.http import int_to_bas class UserCreationForm(forms.ModelForm): """ - A form that creates a user, with no privileges, from the given username and password. + A form that creates a user, with no privileges, from the given email 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)."), + email = forms.EmailField(label=_("Email"), max_length=320, + help_text = _("Required. Must be a valid e-mail address. Max length is 320 characters."), error_message = _("This value must contain only letters, numbers and underscores.")) password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput) password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput) class Meta: model = User - fields = ("username",) + fields = ("email",) - def clean_username(self): - username = self.cleaned_data["username"] + def clean_email(self): + email = self.cleaned_data["email"] try: - User.objects.get(username=username) + User.objects.get(email=email) except User.DoesNotExist: - return username - raise forms.ValidationError(_("A user with that username already exists.")) + return email + raise forms.ValidationError(_("A user with that email already exists.")) def clean_password2(self): password1 = self.cleaned_data.get("password1", "") @@ -44,9 +44,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.")) + email = forms.EmailField(label=_("Email"), max_length=320, + help_text = _("Required. Must be a valid e-mail address. Max length is 320 characters."), + error_message = _("Must be a valid e-mail address.")) class Meta: model = User @@ -54,9 +54,9 @@ class UserChangeForm(forms.ModelForm): class AuthenticationForm(forms.Form): """ Base class for authenticating users. Extend this to get a form that accepts - username/password logins. + email/password logins. """ - username = forms.CharField(label=_("Username"), max_length=30) + email = forms.EmailField(label=_("Email"), max_length=320) password = forms.CharField(label=_("Password"), widget=forms.PasswordInput) def __init__(self, request=None, *args, **kwargs): @@ -71,13 +71,13 @@ class AuthenticationForm(forms.Form): super(AuthenticationForm, self).__init__(*args, **kwargs) def clean(self): - username = self.cleaned_data.get('username') + email = self.cleaned_data.get('email') password = self.cleaned_data.get('password') - if username and password: - self.user_cache = authenticate(username=username, password=password) + if email and password: + self.user_cache = authenticate(email=email, password=password) if self.user_cache is None: - raise forms.ValidationError(_("Please enter a correct username and password. Note that both fields are case-sensitive.")) + raise forms.ValidationError(_("Please enter a correct email and password. Note that both fields are case-sensitive.")) elif not self.user_cache.is_active: raise forms.ValidationError(_("This account is inactive.")) diff -rupN auth/management/commands/createsuperuser.py /home/kyle/django/contrib/auth/management/commands/createsuperuser.py --- auth/management/commands/createsuperuser.py 2009-01-10 11:29:58.000000000 -0700 +++ /home/kyle/django/contrib/auth/management/commands/createsuperuser.py 2009-01-08 22:05:40.000000000 -0700 @@ -12,7 +12,6 @@ from django.core import exceptions from django.core.management.base import BaseCommand, CommandError from django.utils.translation import ugettext as _ -RE_VALID_USERNAME = re.compile('\w+$') EMAIL_RE = re.compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string @@ -24,29 +23,24 @@ def is_valid_email(value): class Command(BaseCommand): option_list = BaseCommand.option_list + ( - make_option('--username', dest='username', default=None, - help='Specifies the username for the superuser.'), make_option('--email', dest='email', default=None, help='Specifies the email address for the superuser.'), make_option('--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind. ' \ - 'You must use --username and --email with --noinput, and ' \ + 'You must use --email with --noinput, and ' \ 'superusers created with --noinput will not be able to log in ' \ 'until they\'re given a valid password.'), ) help = 'Used to create a superuser.' def handle(self, *args, **options): - username = options.get('username', None) email = options.get('email', None) interactive = options.get('interactive') # Do quick and dirty validation if --noinput if not interactive: - if not username or not email: - raise CommandError("You must use --username and --email with --noinput.") - if not RE_VALID_USERNAME.match(username): - raise CommandError("Invalid username. Use only letters, digits, and underscores") + if not email: + raise CommandError("You must use --email with --noinput.") try: is_valid_email(email) except exceptions.ValidationError: @@ -54,50 +48,10 @@ class Command(BaseCommand): password = '' - # Try to determine the current system user's username to use as a default. - try: - import pwd - except ImportError: - default_username = '' - else: - default_username = pwd.getpwuid(os.getuid())[0].replace(' ', '').lower() - - # Determine whether the default username is taken, so we don't display - # it as an option. - if default_username: - try: - User.objects.get(username=default_username) - except User.DoesNotExist: - pass - else: - default_username = '' - - # Prompt for username/email/password. Enclose this whole thing in a + # Prompt for email/password. Enclose this whole thing in a # try/except to trap for a keyboard interrupt and exit gracefully. if interactive: try: - - # Get a username - while 1: - if not username: - input_msg = 'Username' - if default_username: - input_msg += ' (Leave blank to use %r)' % default_username - username = raw_input(input_msg + ': ') - if default_username and username == '': - username = default_username - if not RE_VALID_USERNAME.match(username): - sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n") - username = None - continue - try: - User.objects.get(username=username) - except User.DoesNotExist: - break - else: - sys.stderr.write("Error: That username is already taken.\n") - username = None - # Get an email while 1: if not email: @@ -128,5 +82,5 @@ class Command(BaseCommand): sys.stderr.write("\nOperation cancelled.\n") sys.exit(1) - User.objects.create_superuser(username, email, password) + User.objects.create_superuser(email, password) print "Superuser created successfully." diff -rupN auth/models.py /home/kyle/django/contrib/auth/models.py --- auth/models.py 2009-01-10 11:29:58.000000000 -0700 +++ /home/kyle/django/contrib/auth/models.py 2009-01-10 11:23:17.000000000 -0700 @@ -103,10 +103,10 @@ class Group(models.Model): return self.name class UserManager(models.Manager): - def create_user(self, username, email, password=None): - "Creates and saves a User with the given username, e-mail and password." + def create_user(self, email, password=None): + "Creates and saves a User with the given e-mail and password." now = datetime.datetime.now() - user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now) + user = self.model(None, email.strip().lower(), '', '', 'placeholder', False, True, False, now, now) if password: user.set_password(password) else: @@ -114,8 +114,8 @@ class UserManager(models.Manager): user.save() return user - def create_superuser(self, username, email, password): - u = self.create_user(username, email, password) + def create_superuser(self, email, password): + u = self.create_user(email, password) u.is_staff = True u.is_active = True u.is_superuser = True @@ -131,12 +131,11 @@ class UserManager(models.Manager): class User(models.Model): """Users within the Django authentication system are represented by this model. - Username and password are required. Other fields are optional. + Email 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).")) + email = models.CharField(_('e-mail address'), max_length=320, unique=True, help_text=_("Required. Must be a valid e-mail address. Max Length 320 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) password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the change password form.")) is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site.")) is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts.")) @@ -148,15 +147,31 @@ class User(models.Model): user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True) objects = UserManager() + # We do this so that other modules that expect 'username' can still + # work, but django won't attempt to place a username column in the Database + def __getattr__(self, name): + if name == 'username': + return self.email + else: + return super(User, self).__getattr__(name) + + # This ensures that username and email don't get out of sync + # username is purely a "meta" attribute pointing back at email + def __setattr__(self, name, value): + if name == 'username': + self.email = value + else: + super(User, self).__setattr__(name, value) + class Meta: verbose_name = _('user') verbose_name_plural = _('users') def __unicode__(self): - return self.username + return self.email def get_absolute_url(self): - return "/users/%s/" % urllib.quote(smart_str(self.username)) + return "/users/%s/" % urllib.quote(smart_str(self.email)) def is_anonymous(self): "Always returns False. This is a way of comparing User objects to anonymous users." @@ -312,7 +327,7 @@ class Message(models.Model): class AnonymousUser(object): id = None - username = '' + email = '' is_staff = False is_active = False is_superuser = False