Accounts: add password change form to settings
Uses Django's built-in PasswordChangeForm and update_session_auth_hash so the session stays valid after the change. Form is hidden in a <details> element and opens automatically on validation errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c064d0d4e1
commit
e3a9c61c05
3 changed files with 43 additions and 3 deletions
|
|
@ -13,4 +13,5 @@ urlpatterns = [
|
||||||
path('background/upload/', views.upload_background, name='upload_background'),
|
path('background/upload/', views.upload_background, name='upload_background'),
|
||||||
path('background/delete/', views.delete_background, name='delete_background'),
|
path('background/delete/', views.delete_background, name='delete_background'),
|
||||||
path('focus-station/', views.save_focus_station, name='save_focus_station'),
|
path('focus-station/', views.save_focus_station, name='save_focus_station'),
|
||||||
|
path('change-password/', views.change_password, name='change_password'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import authenticate, login, get_user_model
|
from django.contrib.auth import authenticate, login, get_user_model, update_session_auth_hash
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
|
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
@ -71,6 +71,7 @@ def settings_view(request):
|
||||||
context = {
|
context = {
|
||||||
'profile': profile,
|
'profile': profile,
|
||||||
'has_lastfm': profile.has_lastfm(),
|
'has_lastfm': profile.has_lastfm(),
|
||||||
|
'password_form': PasswordChangeForm(request.user),
|
||||||
}
|
}
|
||||||
return render(request, 'accounts/settings.html', context)
|
return render(request, 'accounts/settings.html', context)
|
||||||
|
|
||||||
|
|
@ -181,6 +182,23 @@ def save_focus_station(request):
|
||||||
return JsonResponse({'ok': True})
|
return JsonResponse({'ok': True})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@require_http_methods(['POST'])
|
||||||
|
def change_password(request):
|
||||||
|
form = PasswordChangeForm(request.user, request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
user = form.save()
|
||||||
|
update_session_auth_hash(request, user)
|
||||||
|
return redirect('settings')
|
||||||
|
profile = request.user.profile
|
||||||
|
return render(request, 'accounts/settings.html', {
|
||||||
|
'profile': profile,
|
||||||
|
'has_lastfm': profile.has_lastfm(),
|
||||||
|
'password_form': form,
|
||||||
|
'password_form_open': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@require_http_methods(['POST'])
|
@require_http_methods(['POST'])
|
||||||
def lastfm_disconnect(request):
|
def lastfm_disconnect(request):
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,28 @@
|
||||||
<section class="settings-section">
|
<section class="settings-section">
|
||||||
<h2>Account</h2>
|
<h2>Account</h2>
|
||||||
<p>Logged in as <strong>{{ request.user.username }}</strong></p>
|
<p>Logged in as <strong>{{ request.user.username }}</strong></p>
|
||||||
<form method="post" action="{% url 'logout' %}" class="inline-form">
|
|
||||||
|
<details {% if password_form_open %}open{% endif %} style="margin-top:1rem;">
|
||||||
|
<summary class="btn" style="display:inline-block;cursor:pointer;">Change password</summary>
|
||||||
|
<form method="post" action="{% url 'change_password' %}" style="margin-top:1rem; display:flex; flex-direction:column; gap:0.6rem; max-width:320px;">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for field in password_form %}
|
||||||
|
<div>
|
||||||
|
<label style="display:block; font-size:0.85rem; margin-bottom:2px;">{{ field.label }}</label>
|
||||||
|
{{ field }}
|
||||||
|
{% if field.errors %}
|
||||||
|
<div class="message message-error" style="margin-top:4px; padding:4px 8px; font-size:0.8rem;">{{ field.errors|join:", " }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% if password_form.non_field_errors %}
|
||||||
|
<div class="message message-error" style="padding:4px 8px; font-size:0.8rem;">{{ password_form.non_field_errors|join:", " }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div><button type="submit" class="btn">Save</button></div>
|
||||||
|
</form>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<form method="post" action="{% url 'logout' %}" class="inline-form" style="margin-top:1rem;">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="btn">Logout</button>
|
<button type="submit" class="btn">Logout</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue