Django user activation doesnt work with token — TypeError: a bytes-like object is required, not 'str'

Question:

So I have the following token activation setup for users to activate their accounts

# urls.py

    # Activation
    path('activate/<uidb64>/<token>/',
         user_views.activate, name='activate'),
# views.py

User = get_user_model()  # Get custom user model

def activate(request, uidb64, token):
    # Debugging
    uid = force_bytes(urlsafe_base64_decode(uidb64))
    print(User.objects.get(pk=uid))
    try:
        uid = force_bytes(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None

    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.email_confirmed = True
        user.save()
        login(request, user)
        return redirect('/dashboard/overview/')
    else:
        return render(request, 'user/authentication/account_activation_invalid.html')
# tokens.py

from django.contrib.auth.tokens import PasswordResetTokenGenerator
import six


class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, user, timestamp):
        return (
                six.text_type(user.pk) + six.text_type(timestamp) +
                six.text_type(user.email_confirmed)
        )


account_activation_token = AccountActivationTokenGenerator()
# models.py
# Custom usermodel

class UserProfile(AbstractBaseUser, PermissionsMixin):

    # Unique identifier
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    # Email and name of the user
    email = models.EmailField(max_length=255, unique=True)
    name = models.CharField(max_length=255)

    # Privilege and security booleans
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)
    email_confirmed = models.BooleanField(default=False)

    objects = UserProfileManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send mail to user - Copied from original class"""
        send_mail(subject, message, from_email, [self.email], **kwargs)

    def __str__(self):
        return self.email

However, when I click the emailed link, the view always returns the isn't valid path.
Some debugging pointed me to print(User.objects.get(pk=uid)) which returns

TypeError: a bytes-like object is required, not ‘str’ [01/Sep/2022
04:18:57] "GET
/activate/MjA1ZjJjOWUtZjJmZC00ZDNlLWI5ZjktMTFiYjJiMzBkYWRm/bb210r-cb574367d34da4d3175ab454a49e6527/
HTTP/1.1" 500 132326

Not sure what’s wrong as this setup already worked in a different project of mine.

Asked By: JSRB

||

Answers:

probably you are need force_str
more here: https://docs.djangoproject.com/en/4.1/ref/utils/#django.utils.encoding.force_str

in your case:

user = User.objects.get(pk=uid.decode("utf-8"))

more here:
https://docs.python.org/3/library/stdtypes.html#bytes.decode

Answered By: Maxim Danilov
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.