Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Modify ObtainAuthToken to use the User model's USERNAME_FIELD and password for authentication instead of assuming username and password.#9674

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
partho-debnath wants to merge3 commits intoencode:main
base:main
Choose a base branch
Loading
frompartho-debnath:fix/token-auth-username-or-email-login

Conversation

@partho-debnath
Copy link

Summary

Django’s default user model usesusername andpassword for authentication. However, when a custom user model is defined withUSERNAME_FIELD = 'email', Django correctly uses email and password for authentication.

The issue was thatDjango REST Framework'sobtain_auth_token endpoint (used for TokenAuthentication) still expectedusername andpassword, even when a custom user model usedemail instead ofusername.

Fix

This update modifies theObtainAuthToken view to dynamically useUSERNAME_FIELD instead of assumingusername. Now, authentication works consistently, whether using the built-in User model or a custom one, with no inconsistencies.

Changes Made:

  1. ObtainAuthToken now retrieves theUSERNAME_FIELD from the user model.
  2. It authenticates usingUSERNAME_FIELD andpassword, ensuring compatibility with both built-in and custom user models.

Impact

  1. Users with a custom User model (e.g.,USERNAME_FIELD = 'email') can now log in usingemail andpassword instead ofusername andpassword.
  2. Users with thedefault User model or acustom User model whereUSERNAME_FIELD = 'username' can continue logging in usingusername andpassword as expected.

…password' instead of 'username' and 'password' for both the built-in and custom User models
@partho-debnath
Copy link
Author

If any changes are required, please let me know.

@browniebroke
Copy link
Member

I'm not sure this falls under ourcurrent maintenance policy:

At this point in its lifespan we consider Django REST framework to be feature-complete. We focus on pull requests that track the continued development of Django versions, and generally do not accept new features or code formatting changes.

One could argue that this improves compatibility with custom Django user models, but on the other handUSERNAME_FIELD has been in Django for so long that it's a bit late to add this now...

It's also simple to customise in user-land right now, and this is explained in our docs.

If we were to ever accept it, we would need some tests to cover the behaviour with a customised user model...

@partho-debnath
Copy link
Author

partho-debnath commentedMar 31, 2025
edited
Loading

Thank you for reviewing my pull request and for your feedback. I understand that Django REST Framework is considered feature-complete and that new features are usually only accepted if they align with Django’s ongoing development.

However, I see this change as more of a compatibility improvement rather than a new feature. WhileUSERNAME_FIELD has been in Django for a long time,ObtainAuthToken still assumes ausername by default. This creates aninconsistency for projects that take advantage of Django’s built-in flexibility forcustom user models.

@browniebroke, Are you saying that it won't be possible to accept or merge this change?

Copy link
Collaborator

@auvipyauvipy left a comment

Choose a reason for hiding this comment

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

I think this might break existing projects or functionalities

@stale
Copy link

stalebot commentedOct 18, 2025

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stalestalebot added the stale labelOct 18, 2025
@auvipyauvipy requested a review fromCopilotDecember 14, 2025 15:04
@auvipyauvipy removed the stale labelDec 14, 2025
Copy link

CopilotAI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR modifies theObtainAuthToken view andAuthTokenSerializer to support custom Django user models that use a differentUSERNAME_FIELD (e.g., 'email' instead of 'username'). The changes enable dynamic authentication field detection based on the user model'sUSERNAME_FIELD attribute.

  • Dynamically retrieves theUSERNAME_FIELD from the active user model instead of hardcoding 'username'
  • Updates the serializer to create appropriate fields (EmailField or CharField) based on the USERNAME_FIELD type
  • Modifies the API schema generation to reflect the correct field name in documentation

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.

FileDescription
rest_framework/authtoken/serializers.pyRefactored to dynamically create authentication fields based on USERNAME_FIELD and pass credentials to authenticate()
rest_framework/authtoken/views.pyUpdated schema definition to use USERNAME_FIELD for API documentation
docs/api-guide/authentication.mdAdded documentation note about custom user model support

💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.

)
def__init__(self,instance=None,data=None,**kwargs):
super().__init__(instance,data=data,**kwargs)
self.identifier_fiend_name=USER_MODEL.USERNAME_FIELD

Choose a reason for hiding this comment

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

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +14
ifUSER_MODEL.get_email_field_name()==self.identifier_fiend_name:
self.fields[self.identifier_fiend_name]=serializers.EmailField(

Choose a reason for hiding this comment

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

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
write_only=True
)
else:
self.fields[self.identifier_fiend_name]=serializers.CharField(

Choose a reason for hiding this comment

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

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
username=username,password=password)
ifidentifier_valueandpassword:
credentials= {
self.identifier_fiend_name:identifier_value,

Choose a reason for hiding this comment

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

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.

fromrest_frameworkimportserializers

USER_MODEL=get_user_model()

Choose a reason for hiding this comment

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

The USER_MODEL is evaluated at module import time, which means if the user model changes dynamically or if this module is imported before Django apps are ready, it could reference the wrong model. Consider moving this inside theinit method where it's actually used, similar to how it's done in the views.py file.

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +28
def__init__(self,instance=None,data=None,**kwargs):
super().__init__(instance,data=data,**kwargs)
self.identifier_fiend_name=USER_MODEL.USERNAME_FIELD
ifUSER_MODEL.get_email_field_name()==self.identifier_fiend_name:
self.fields[self.identifier_fiend_name]=serializers.EmailField(
label=_(self.identifier_fiend_name.title()),
write_only=True
)
else:
self.fields[self.identifier_fiend_name]=serializers.CharField(
label=_(self.identifier_fiend_name.title()),
write_only=True
)
self.fields["password"]=serializers.CharField(
label=_("Password"),
style={'input_type':'password'},
trim_whitespace=False,
write_only=True
)

Choose a reason for hiding this comment

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

The new behavior for supporting custom user models with different USERNAME_FIELD values (e.g., 'email') lacks test coverage. Consider adding tests that verify the serializer correctly handles custom user models where USERNAME_FIELD is set to a field other than 'username', such as 'email'.

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +22
USER_MODEL=get_user_model()
identifier_field_name=USER_MODEL.USERNAME_FIELD

Choose a reason for hiding this comment

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

The USER_MODEL and identifier_field_name are evaluated at class definition time inside the conditional block. This means they are computed once when the module is imported. If the user model is swapped or changed dynamically, this will not be reflected. Consider moving this logic to a method or property that evaluates at runtime.

Copilot uses AI. Check for mistakes.
Comment on lines 23 to 32
schema=ManualSchema(
fields=[
coreapi.Field(
name="username",
name=identifier_field_name,
required=True,
location='form',
schema=coreschema.String(
title="Username",
description="Validusername for authentication",
title=identifier_field_name.title(),
description=f"Valid{identifier_field_name} for authentication",
),

Choose a reason for hiding this comment

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

The new dynamic schema generation based on USERNAME_FIELD lacks test coverage. Consider adding tests that verify the schema correctly reflects the USERNAME_FIELD from custom user models (e.g., when USERNAME_FIELD is 'email', the schema should show 'email' instead of 'username').

Copilot uses AI. Check for mistakes.

defvalidate(self,attrs):
username=attrs.get('username')
identifier_value=attrs.get(self.identifier_fiend_name)

Choose a reason for hiding this comment

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

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
raiseserializers.ValidationError(msg,code='authorization')
else:
msg=_('Must include "username" and "password".')
msg=_(f'Must include "{self.identifier_fiend_name}" and "password".')

Choose a reason for hiding this comment

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

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

Copilot code reviewCopilotCopilot left review comments

@auvipyauvipyauvipy requested changes

Requested changes must be addressed to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

3 participants

@partho-debnath@browniebroke@auvipy

[8]ページ先頭

©2009-2025 Movatter.jp