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.