Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions rest_framework/authtoken/serializers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.contrib.auth import authenticate
from django.contrib.auth import authenticate, get_user_model
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers
Expand All @@ -20,13 +20,25 @@ class AuthTokenSerializer(serializers.Serializer):
read_only=True
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
User = get_user_model()
username_field = User.USERNAME_FIELD
if username_field != 'username':
self.fields[username_field] = self.fields.pop('username')
self.fields[username_field].label = _(username_field.capitalize())

Comment on lines +26 to +38
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

self.fields[username_field].label = _(username_field.capitalize()) builds a dynamic translation key at runtime, so it won’t be picked up by Django’s i18n tooling and will remain untranslated. Also, capitalize() produces awkward labels for fields like phone_number. Prefer using the user model field’s verbose_name (e.g., User._meta.get_field(username_field).verbose_name) and avoid wrapping dynamically generated strings in gettext_lazy.

Copilot uses AI. Check for mistakes.
def validate(self, attrs):
username = attrs.get('username')
User = get_user_model()
username_field = User.USERNAME_FIELD
username = attrs.get(username_field)
password = attrs.get('password')
Comment on lines +41 to 43
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

The local variable name username is now potentially an email/phone/etc (depending on USERNAME_FIELD), which makes the control flow harder to follow. Consider renaming it to something neutral like identifier/login (and similarly for username_field if you store it on self).

Copilot uses AI. Check for mistakes.

if username and password:
user = authenticate(request=self.context.get('request'),
username=username, password=password)
user = authenticate(
request=self.context.get('request'),
**{username_field: username, 'password': password}
)
Comment on lines +23 to +49
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

New behavior for non-default USERNAME_FIELD (renaming the input field and authenticating via **{username_field: ...}) isn’t covered by tests. Since this is a regression fix for custom user models, add a test that patches get_user_model().USERNAME_FIELD (e.g. to email) and asserts the serializer accepts {email, password} and calls authenticate with the expected keyword argument.

Copilot uses AI. Check for mistakes.

# The authenticate call simply returns None for is_active=False
# users. (Assuming the default ModelBackend authentication
Expand All @@ -35,7 +47,7 @@ def validate(self, attrs):
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg, code='authorization')
else:
msg = _('Must include "username" and "password".')
msg = _(f'Must include "{username_field}" and "password".')
raise serializers.ValidationError(msg, code='authorization')
Comment on lines 57 to 59
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

_(f'Must include "{username_field}" and "password".') creates a different msgid depending on USERNAME_FIELD, which breaks translations. Use a constant translatable string with interpolation (e.g., a %(username_field)s placeholder) and consider using the field’s human-readable label/verbose_name in the message rather than the internal model field name.

Copilot uses AI. Check for mistakes.

attrs['user'] = user
Expand Down
Loading