2

I am currently learning Django, mainly from Andrew Pinkham's book "Django Unleashed", and because the book is prior to Django 2, I cannot use the code in verbatim but adjust the code accordingly. I am currently on Ch. 21 (Extending Authentication). Everything works so far, but I am having trouble with users resetting their passwords. A user can enter the email for resetting the password, and a message appears, stating the email was successfully sent -- but nothing is displayed on the console (the code is still in DEBUG and I have yet to allow emails to be actually sent, other than to the console for testing).

Here is an excerpt of my project structure:

pysawitweb
|_contact
|_pysawitweb
|  |_settings.py
|  |_urls.py
|  |_...
|_user
|  |_templates
|  |  |_user
|  |  |  |_base_user.html
|  |  |  |_logged_out.html
|  |  |  |_login.html
|  |  |  |_login_form.html
|  |  |  |_password_change_done.html
|  |  |  |_password_change_form.html
|  |  |  |_password_reset_complete.html
|  |  |  |_password_reset_confirm.html
|  |  |  |_password_reset_email.txt
|  |  |  |_password_reset_form.html
|  |  |  |_password_reset_sent.html
|  |  |  |_password_reset_subject.txt
|  |_urls.py
|  |_...
|_manage.py

In the settings.py file:

...
DEBUG = True

ALLOWED_HOSTS = []

# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'contact',
    'weather',
    'organizer',
    'user',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'pysawitweb.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.media',
            ],
        },
    },
]

WSGI_APPLICATION = 'pysawitweb.wsgi.application'

# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Email
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
SERVER_EMAIL = 'contact@django-unleashed.com'
DEFAULT_FROM_EMAIL = 'no-reply@django-unleashed.com'
EMAIL_SUBJECT_PREFIX = '[PySawit Web] '
MANAGERS = (
    ('Us', 'ourselves@django-unleashed.com'),
)


# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us'

USE_I18N = True

USE_L10N = True

USE_TZ = True

TIME_ZONE = 'Asia/Kuala_Lumpur'


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]


# MEDIA
MEDIA_URL = '/media/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')


#LOGIN
LOGIN_REDIRECT_URL = reverse_lazy('organizer_opd_list')

LOGIN_URL = reverse_lazy('dj-auth:login')

LOGOUT_URL = reverse_lazy('dj-auth:logout')

Everything works: a user can login, logout, and change password, but trying to reset it fails to send an email message to the console (no errors appear). I have a contact app (from Ch. 11 of Django Unleashed book) that allows users to send me an email -- and that works perfectly, using the same settings as in settings.py file above, to display the email on the console.

Here is from pysawitweb/urls.py:

from user import urls as user_urls
...

urlpatterns = [
    ...
    path('user/', include(user_urls, namespace='dj-auth')),
]

and in the user/urls.py:

from django.contrib.auth import views as auth_views
from django.contrib.auth.forms import AuthenticationForm
from django.urls import include, path, reverse_lazy
from django.views.generic import RedirectView


password_urls = [
    path('',
         RedirectView.as_view(
             pattern_name='dj-auth:pw_reset_start',
             permanent=False)),

    path('change/',
         auth_views.password_change,
         {
             'template_name': 'user/password_change_form.html',
             'post_change_redirect': reverse_lazy('dj-auth:pw_change_done')
         },
         name='pw_change'),

    path('change/done/',
         auth_views.password_change_done,
         {
             'template_name': 'user/password_change_done.html'
         },
         name='pw_change_done'),

    path('reset/',
         auth_views.password_reset,
         {
             'template_name': 'user/password_reset_form.html',
             'email_template_name': 'user/password_reset_email.txt',
             'subject_template_name': 'user/password_reset_subject.txt',
             'post_reset_redirect': reverse_lazy('dj-auth:pw_reset_sent')
         },
         name='pw_reset_start'),

    path('reset/sent/',
         auth_views.password_reset_done,
         {
             'template_name': 'user/password_reset_sent.html'
         },
         name='pw_reset_sent'),

    path('reset/'
         '(<uidb64>[0-9A-Za-z_\-]+)/'
         '(<token>[0-9A-Za-z]{1,13}'
         '-[0-9A-Za-z]{1,20})/',
         auth_views.password_reset_confirm,
         {
             'template_name': 'user/password_reset_confirm.html',
             'post_reset_redirect': reverse_lazy('dj-auth:pw_reset_complete')
         },
         name='pw_reset_confirm'),

    path('reset/done/',
         auth_views.password_reset_complete,
         {
             'template_name': 'user/password_reset_complete.html',
             'extra_context': {'form': AuthenticationForm}
         },
         name='pw_reset_complete'),
]


app_name = 'user'


urlpatterns = [
    path('', RedirectView.as_view(pattern_name='dj-auth:login', permanent=False)),

    path('login/',
         auth_views.login,
         {
             'template_name': 'user/login.html'
         },
         name='login'),

    path('logout/',
         auth_views.logout,
         {
             'extra_context': {'form': AuthenticationForm},
             'template_name': 'user/logged_out.html'
         },
         name='logout'),

    path('password/', include(password_urls)),
]

In the user/login.html file, users can click on the "Forgot password?" link which directs them to user/password_reset_form.html to enter their email and submit them. A user/password_reset_sent.html is then rendered that tells them the email have been sent to them -- but nothing (not even error) appears on the console.

In the user/login.html:

{% extends parent_template|default:"user/base_user.html" %}

{% block title %}Login - {{ block.super }}{% endblock %}

{% block content %}
  {% include "user/login_form.html" %}
  <p><a href="{% url 'dj-auth:pw_reset_start' %}">Forgotten password?</a></p>
{% endblock %}

and in the user/password_reset_form.html:

{% extends parent_template|default:"user/base_user.html" %}

{% block title %}Reset Password - {{ block.super }}{% endblock %}

{% block content %}
  <p>Reset your password by email.</p>
  <form action="{% url 'dj-auth:pw_reset_start' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Send Me Reset Instructions</button>
  </form>
{% endblock %}

and lastly, in the user/password_reset_sent.html:

{% extends parent_template|default:"user/base_user.html" %}

{% block title %}Password Reset Sent - {{ block.super }}{% endblock %}

{% block content %}
  <p>Password reset email sent!</p>
  <p>Please check your inbox and spam box.</p>
{% endblock %}

Text files password_reset_email.txt and password_reset_subject.txt are respectively as follows:

Hello from {{ site_name }}!

We've received a request to reset {{ user.get_username }}'s password.

If you did not request a password reset, please ignore this message.

To reset your password, please navigate to:

{{ protocol }}://{{ domain }}{% url 'dj-auth:pw_reset_confirm' uid token %}

and

{{ site_name }} Password Reset

Can someone diagnose the problem why no email messages are sent to the console, though my contact app, using the same EMAIL_BACKEND in the setting.py works?

Thanks for even reading this.

cbsteh
  • 809
  • 6
  • 19

1 Answers1

4

Solved it. Reading the docs here showed Django will only send emails to users with registered emails, unlike in Contact. Sheesh ...

cbsteh
  • 809
  • 6
  • 19