Include default password in Welcome email for new users

I’m sorry, I’m not seeing in the code you’ve posted where / how you’re actually creating / sending this email. Nor do I understand how or why a form might be involved.

Please post all of the code, templates, functions, forms, models, etc that shows how this email is being created and sent.

Okay. This is my urls.py. It contains the paths for the register page where I create an account as well as the path for resetting the password.

urlpatterns = [
     path('login/',views.loginPage, name="login"),
     path('register/',views.registerPage, name="register"),
     path('logout/',views.logoutUser, name="logout"),

     path('dashboard/',views.dashboardPage, name="dashboard"),
     path('account_settings/',views.accountSettings, name="accountSettings"),


     path("reset_password/",
      auth_views.PasswordResetView.as_view(template_name="accounts/password_reset.html"),
       name="reset_password"),

     path("reset_password_sent/",
      auth_views.PasswordResetDoneView.as_view(template_name="accounts/password_reset_sent.html"),
       name="password_reset_done"),

     path("reset/<uidb64>/<token>/",
     auth_views.PasswordResetConfirmView.as_view(template_name="accounts/password_reset_form.html"),
      name="password_reset_confirm"),

     path("reset_password_complete/",
     auth_views.PasswordResetCompleteView.as_view(template_name="accounts/password_reset_done.html"),
      name="password_reset_complete"),
]

This is the code for the form I use to create users in the register page.

class CreateUserForm(UserCreationForm):

    #makes the password input optional
    def __init__(self, *args, **kwargs):
        super(UserCreationForm, self).__init__(*args, **kwargs)
        
        self.fields['password1'].required = False
        self.fields['password2'].required = False
        # If one field gets autocompleted but not the other, our 'neither
        # password or both password' validation will be triggered.
        self.fields['password1'].widget.attrs['autocomplete'] = 'off'
        self.fields['password2'].widget.attrs['autocomplete'] = 'off'


    password1 = forms.CharField(widget = forms.PasswordInput(attrs ={'placeholder': 'Password'}))
    password2 = forms.CharField(widget = forms.PasswordInput(attrs ={'placeholder': 'Please retype your password'}))

    class Meta:
        model=User
        fields = ['username','email','password1','password2','first_name','last_name']
        widgets = {
            "username": forms.TextInput(attrs={'placeholder': 'Username'}),
            "email": forms.EmailInput(attrs={'placeholder': 'Email'}),
            "first_name": forms.TextInput(attrs={'placeholder': 'First Name'}),
            "last_name": forms.TextInput(attrs={'placeholder': 'Last Name'}),
        }

This is my register.html file. Here I create an account without setting the password. The password is randomly set in the views.py (which is coming up).

{% extends 'home/base.html' %}
{% load static %}

{% block content %}

<section id="register">
    <div class="container">
        <div class="register_box">
            <div class="form_heading">
                <h1>Register</h1>
            </div>
            <form action="" method="POST">
                {% csrf_token %}

                {% for non_field_error in form.non_field.errors %}
                    <p class="error_message">{{ non_field_error }}</p>
                {% endfor %}

                <div class="field_wrapper">
                    {{form.username}}
                    {% for error in form.username.errors %}
                        <p class="error_message">{{ error }}</p>
                    {% endfor %}
                </div>

                <div class="field_wrapper">
                    {{form.email}}
                    {% for error in form.email.errors %}
                        <p class="error_message">{{ error }}</p>
                    {% endfor %}
                </div>

               <!-- <div class="field_wrapper">
                    {{form.password1}}
                    {% for error in form.password1.errors %}
                        <p class="error_message">{{ error }}</p>
                    {% endfor %}
                </div>

                <div class="field_wrapper">
                    {{form.password2}}
                    {% for error in form.password1.errors %}
                        <p class="error_message">{{ error }}</p>
                    {% endfor %}
                </div> -->

                <div class="field_wrapper">
                    {{form.first_name}}
                    {% for error in form.first_name.errors %}
                        <p class="error_message">{{ error }}</p>
                    {% endfor %}
                </div>

                <div class="field_wrapper">
                    {{form.last_name}}
                    {% for error in form.last_name.errors %}
                        <p class="error_message">{{ error }}</p>
                    {% endfor %}
                </div>
                
                <button type="submit" class="success_btn"> Register </button>
                
                <p> Already have an account? <a href="{% url 'login' %}" class="help_text"> Login Here</a> </p>
            </form>
        </div>
        
    </div>
</section>

this is the function for the registering the user in the views.py.

@staff_member_required 
def registerPage(request):
    if not request.user.is_authenticated or request.user.username == 'admin_username':
        form = CreateUserForm()

        if request.method == "POST":
            form=CreateUserForm(request.POST)
            if form.is_valid():
                
                user = form.save()
                user.set_password(get_random_string())
                user.save()


                username = form.cleaned_data.get("username")
                email = form.cleaned_data.get("email")
                name = form.cleaned_data.get("first_name")

                url = request.build_absolute_uri('reset/<uidb64>/<token>/')

                set_password_form  = PasswordResetForm({'email':email,'url':url , 'username':username,"name":name})

                assert set_password_form.is_valid()
                set_password_form.save(
                    request=request,
                    from_email="email",
                    email_template_name="accounts/welcome_email_template.html",
                    html_email_template_name="accounts/welcome_email_template.html",
                )
                   

                messages.success(request,"Account was created for " + username )
                return redirect("login")


        context= {"form":form}
        return render(request, 'accounts/register.html', context)
    else:
        return redirect('home')

And finally, this is my welcome_email_template.html which is passed to the passwordresetform and contains the link for the new user to reset their password.


Hello {{name}},

The  team would like to officially welcome you on board as investor.

We are pleased to inform you that your online investment account is now active.

Please click on the link below, which will guide you into setting up your password. The details for your account are also listed below.

{{url}}


username  : {{username}}
email  : {{email}}


If you have any further questions or clarification on this matter, kindly reach out to us at random@gmail.com.

Happy Investing!

I believe that should be all the code relating to the automatic email functionality. If there is anything else you need, I will add it. And thank you very much for the support so far.

I’m sorry, I’m not seeing where you’re actually rendering that email and sending it out.

You’re also referencing a “PasswordResetForm” here that appears to be where you’re using the data created, but I don’t see it in anything you’ve posted.

I am not sure what you mean by “where I am actually rendering that email and sending it out”. But with regards to the PasswordResetForm , I just imported the default form.

from django.contrib.auth.forms import PasswordResetForm

Ok, I’m starting to see what’s going on here.

You’re using the standard PasswordResetForm:

Looking at the source code for the PasswordResetForm class, its calling sequence is:

    def save(self, domain_override=None,
             subject_template_name='registration/password_reset_subject.txt',
             email_template_name='registration/password_reset_email.html',
             use_https=False, token_generator=default_token_generator,
             from_email=None, request=None, html_email_template_name=None,
             extra_email_context=None):

Any data you want rendered in the emails can be added into the .save call through the extra_email_context parameter.

The docs for PasswordResetForm references the PasswordResetView docs for the parameters initially available. (see the section on Email template context)

Thank you. I added the data to extra_email_context and the email sent now contains the right data. I just have one final issue.
Since I set the url to be url =request.build_absolute_uri('reset/<uidb64>/<token>/') from the register page view, the url in the email outputs ,

http://127.0.0.1:8000/register/reset/%3Cuidb64%3E/%3Ctoken%3E/

How should I remove the register path? I only need the path up until the current domain and the rest should be the reset password path that I added to the url.

Put the leading slash in front of the path in build_absolute_uri → ‘/reset/…’

Okay. Now the link takes me to the password reset form page. However now, the fields for entering the new password doesn’t show up for some reason. I checked the url of the page and this is what it showed.
image
If I try to copy and paste the url anywhere, I get this though.

http://127.0.0.1:8000/reset/%3Cuidb64%3E/%3Ctoken%3E/

Yes, you need to supply the actual uidb64 and token variables - perhaps using the reverse method to generate the url. That URL needs to contain the “real” values that belong there, not those placeholders.

Okay so searched online for how to make the uid and tokens and I manage to create the url like this. (Note: Below is just an extract from the view function.)

from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.contrib.auth.tokens import default_token_generator

                uid = urlsafe_base64_encode(force_bytes(user.pk))
                token =  default_token_generator.make_token(user)

                reset_view = reverse('password_reset_confirm', args=[uid, token])

                url = request.build_absolute_uri( reset_view )

I tried this and it works but I just want your opinion whether I went about it the right way and if not, what changes should I make?

Looks perfectly valid to me.

However, now that I understand how you’re using the default form for this, and that the .save method supplies those variables, you could go back to the “standard formulation” of building the URL in the template.

e.g.:
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

Either one’s fine.

Thank you very much for the support. The welcome email functionality is working now.

1 Like