Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #26719 -- Normalized email in AbstractUser.clean(). #6788

Closed
wants to merge 1 commit into from

Conversation

tamhuynhit
Copy link

@tamhuynhit tamhuynhit commented Jun 16, 2016

@claudep
Copy link
Member

claudep commented Jun 18, 2016

I wonder what's the more correct way to do that, the save() or the clean() method?
We might get more guaranties that normalization occurs with the save() method at the expense of calling normalization methods a bit too much. clean() might be a more fine-grained approach as we are essentially protecting against user input which should pass through clean() (via ModelForm).

@@ -145,7 +145,6 @@ def _create_user(self, username, email, password, **extra_fields):
if not username:
raise ValueError('The given username must be set')
email = self.normalize_email(email)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this line should also disappear?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Yes, thanks a lot. Somehow I forgot to delete that line after several double checks 😞

@timgraham
Copy link
Member

Normalizing in clean() seems more expected to me so that any user code would see the normalized values. On the other hand, normalizing in the manager as is done now seems like a good idea too so that things like the createsuperuser command are protected.

@@ -361,6 +360,13 @@ def email_user(self, subject, message, from_email=None, **kwargs):
"""
send_mail(subject, message, from_email, [self.email], **kwargs)

def save(self, *args, **kwargs):
if self.email:
self.email = UserManager.normalize_email(self.email)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to account for the possibility of a custom manager with something like self.__class__._default_manager.normalize_email.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your suggestion. It looks better to me, will fix this in the next commit

@claudep
Copy link
Member

claudep commented Jun 18, 2016

On the other hand, normalizing in the manager as is done now seems like a good idea too so that things like the createsuperuser command are protected.

Sure, in my local patch, I'm calling user.clean() in the manager.

@claudep
Copy link
Member

claudep commented Jun 19, 2016

FYI here's my current local patch: https://gist.github.com/claudep/78e3661224a9b1767b8b0ea3b8ac8e9b

@tamhuynhit
Copy link
Author

@claudep 👍 Thanks for the patch. I will update the code with normalization happen in clean method. But I have some problem with testing from the discussion above.

@tamhuynhit tamhuynhit changed the title Fixed #26719 -- Call normalize email and username in User.save Fixed #26719 -- Call normalize email and username in User.clean Jun 20, 2016
@timgraham
Copy link
Member

Please let me know if you have time to work on this in the next 8 hours. If not, I can pick it up since we're trying to get the beta release out today. Thanks!

@tamhuynhit
Copy link
Author

@timgraham Thanks, I am working on it, will push the code soon

@@ -111,6 +111,7 @@ def clean_password2(self):
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.clean()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't expect this would be needed since Model.clean() is called as part of ModelForm validation (the test passes with this commented out). Was it just an oversight on your part?

@timgraham
Copy link
Member

I'll open a PR to your branch for these changes.

@timgraham timgraham changed the title Fixed #26719 -- Call normalize email and username in User.clean Fixed #26719 -- Normalized email and username in AbstractUser.clean(). Jun 21, 2016
@timgraham
Copy link
Member

Edits at tamhuynhit#1. I think this is good to go after that although if @claudep can take a look, that would be welcome.

@tamhuynhit
Copy link
Author

tamhuynhit commented Jun 21, 2016

PR is merged. Thanks for helping me on this @timgraham @claudep

@claudep
Copy link
Member

claudep commented Jun 21, 2016

Looks better and better! The remaining questions for me:

  • Does it make sense to keep having the normalize methods on the manager instead of on the AbstractUser model itself?
  • Should we normalize the username based on get_username(), maybe even on AbstractBaseUser.clean() level?

@tamhuynhit
Copy link
Author

tamhuynhit commented Jun 21, 2016

@claudep About the first question, I think you make sense to have that concern since the current code to call normalize_email have to go through many steps: self.__class__.objects.normalize_email. Move them to AbstractBaseUser seems good to me.
I didn't realize we have get_username before. I try to find some edge cases when USERNAME_FIELD point to any special field but not found yet. However if we want to use get_username, I think we should modify get_username with an empty default value, or force_text function will return a string 'None':

def get_username(self):
    return getattr(self, self.USERNAME_FIELD, '')

Move them up to AbstractBaseUser is a good idea. So should we make these changes happen ?

@timgraham
Copy link
Member

I think I'll do a separate PR to make the changes for username. Then we can rebase this PR and make the changes for email which is more complicated with respect to backwards-compatibility if we want to move the manager method. Changing how email is normalized also isn't a release blocker for 1.10.

@timgraham
Copy link
Member

Patch for username changes only: #6819

@timgraham timgraham changed the title Fixed #26719 -- Normalized email and username in AbstractUser.clean(). Fixed #26719 -- Normalized username in AbstractUser.clean(). Jun 21, 2016
@timgraham
Copy link
Member

With the other patch merged, you can either rebase your branch and force push, or open a new PR to continue the changes for normalizing email.

@@ -890,6 +890,11 @@ Miscellaneous
* If you subclass ``AbstractBaseUser`` and override ``clean()``, be sure it
calls ``super()``. :meth:`.AbstractBaseUser.normalize_username` is called in
a new :meth:`.AbstractBaseUser.clean` method.
* If you subclass ``AbstractUser`` and override ``clean()``, be sure it calls
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're targeting 1.11 for this change. This only needs to mention email now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this to the 1.11 release notes and also update the versionadded annotation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I've moved them to 1.11, you can check again if I didn't make it right

@timgraham
Copy link
Member

You can squash commits when updating too.

@tamhuynhit tamhuynhit force-pushed the ticket_26719 branch 2 times, most recently from caa6ab7 to ebb2487 Compare June 24, 2016 02:44
@timgraham timgraham changed the title Fixed #26719 -- Normalized username in AbstractUser.clean(). Fixed #26719 -- Normalized email in AbstractUser.clean(). Jun 24, 2016
@timgraham
Copy link
Member

merged in 09119df, thanks!

@timgraham timgraham closed this Jun 24, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants