base64 user ID object error for email activation

An activation email is sent and I have logged that it comes back in when the link is selected. However, it gives an error when decoding it.

This code:
uid = force_str(urlsafe_base64_decode(uidb64))

gets this error:

Field ‘id’ expected a number but got b’<property object at 0x000002553FA59DA0>'.
The above exception (invalid literal for int() with base 10: b’<property object at 0x000002553FA59DA0>') was the direct cause of the following exception:

Code:

views.py

            current_site = get_current_site(self.request)
            subject = 'Activate your account'
            message = render_to_string('account_activation_email.html', {
                'user': CustomUser,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(CustomUser.pk)),
                'token': account_activation_token.make_token(CustomUser),
            })

            send_mail(subject, message, 'xxxxxxxxxxxxxxxx', [user_email])

def account_activation_sent(request):
    return render(request, 'account_activation_sent.html')


def activate(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = CustomUser.objects.get(pk=uid)

    except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
        user = None

    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.save()
        login(request, user)
        return redirect('account_activation_complete')
    else:
        return HttpResponseBadRequest('Activation link is invalid!')


def account_activation_complete(request):
    return render(request, 'account_activation_complete.html') 

HTML

{% load static %}

{% autoescape off %}

Hello {{ user.username }},

Please click the link below to activate your account:

{% block reset_link %}

<a href="http://{{ domain }}{% url 'activate' uidb64=uid token=token %}">Activate your account"></a>

{% endblock %}

If you did not register on our site, please ignore this message.

Best regards,

Your Website Team

{% endautoescape %}

app/urls

urlpatterns = [
    path('signup/<str:alias_name>', views.SignUpView.as_view(), name='signup.html'),
    path('activate/<uidb64>/<token>/', views.activate, name='activate'),
    path('account_activation_sent/', views.account_activation_sent, name='account_activation_sent'),
    path('account_activation_complete/', views.account_activation_complete, name='account_activation_complete'),
]

Side note: You need to use the backtick - ` and not the apostrophe, and the three backticks need to be on lines by themselves.

Can you show one of the urls that was created? That may also help identify the cause of the problem here.

Hello ,

Please click the link below to activate your account:

<a href="http://127.0.0.1:8000/accounts/activate/PHByb3BlcnR5IG9iamVjdCBhdCAweDAwMDAwMjU4QUVCMTlERjA-/bvlykp-1625309ef43363150a239bcad9101410/">Activate your account"></a>

Please post the complete view - the top appears to be missing.

But the basic issue is - you have:

I’m guessing that CustomUser is the name of the model and not an instance of CustomUser, in which case that is the problem here.

I would agree and how would I get the instance?

Please post that complete view.

In general, if pk is the primary key for the instance, you can query that model for the instance with that as the primary key.

from .forms import CustomUserCreationForm
from .models import CustomUser
from .tokens import account_activation_token
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import send_mail
from django.http import HttpResponseBadRequest
from django.shortcuts import render, redirect
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.utils.encoding import force_bytes, force_str
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.views.generic.edit import CreateView
import re


class SignUpView(CreateView):
    form_class = CustomUserCreationForm
    success_url = reverse_lazy("login")
    template_name = "signup.html"
    model = CustomUser

    def form_valid(self, form):
        form.save()
        user_email = CustomUser.objects.filter(username=form.cleaned_data['username']).values('email')[0]['email']
        
        current_site = get_current_site(self.request)
        subject = 'Activate your account'
        message = render_to_string('account_activation_email.html', {
            'user': CustomUser,
            'domain': current_site.domain,
            'uid': urlsafe_base64_encode(force_bytes(CustomUser.pk)),
            'token': account_activation_token.make_token(CustomUser),
        })

        send_mail(subject, message, 'xxxxxxxxxxx', [user_email])

        return redirect('account_activation_sent')



def account_activation_sent(request):
    return render(request, 'account_activation_sent.html')


def activate(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = CustomUser.objects.get(pk=uid)

    except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
        user = None

    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.save()
        login(request, user)
        return redirect('account_activation_complete')
    else:
        return HttpResponseBadRequest('Activation link is invalid!')


def account_activation_complete(request):
    return render(request, 'account_activation_complete.html') 

Looking at your form_valid function:

The ModelForm.save method returns the object being saved.

Therefore, changing that line to new_user = form.save() gives you the new user that you just created.

That means you don’t need the user_email line, because you have access to that field as user_email = new_user.email

That means your context for your render_to_string call can look like this:

{
    'user': new_user,
    'domain': current_site.domain,
    'uid': urlsafe_base64_encode(force_bytes(new_user.pk)),
    'token': account_activation_token.make_token(new_user),
}

Wow, that did it. Thanks so much.