除了password reset, password change, 當然還有修改用戶的資料。
Django更改用戶資料跟用戶註冊的原理差不多,都是使用內建的Form:
django.contrib.auth.forms.UserChangeForm
但內建的
UserChangeForm其實是給superuser例如admin用的,所以表單內容會比較多 - 如上圖。
為了讓用戶自行更改資料,我們可以新增一個subclass -
UserAccount。
1.
[Set url]
先到
urls.py
新增urlspattern:
from <app> import views as myviews
urlspattern=[
...
...
path('myaccount/', myviews.myaccount, name='myaccount'),
]
2.
[Create view function]
新建一個views function -
myaccount:
from .forms import UserAccount
def myaccount(request):
if request.method == "POST":
form = UserAccount(request.POST, instance=request.user)
if form.is_valid():
form.save()
return appindex(request)
else:
error = 'There is something wrong about your information.'
form = UserAccount(instance=request.user)
return render(request, 'registration/myaccount.html', {'form':form, 'error':error})
else:
form = UserAccount(instance=request.user)
return render(request, 'registration/myaccount.html', {'form':form})
留意這裡我們除了request.POST,還加了
instance=request.user
因為這一次我們希望把用戶既有的資料,預先填寫在form裡面,所以我們要強調instance。
3.
[Create new form]
然後到
<app>.forms
開設一個class -
UserAccount為UserChangeForm的subclass:
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import User
class UserAccount(UserChangeForm):
username = forms.CharField(required=True, disabled=True)
email = forms.EmailField(required=False, help_text='You may update your email address here')
first_name = forms.CharField(required=False, help_text='You may edit your nickname here for display', label='Nickname')
password = forms.CharField(
required=False,
help_text="You may change your password <a href='registration/password_change'>here</a>"
)
class Meta:
model = User
fields = ('username', 'first_name', 'email', 'password',)
這裡我們利用subclass
UserAccount overrides UserChangeForm原本的表單。
先是把我們希望有的fields都加上自行寫help_text,required都改為False,因為我們不一定需要用戶更改了甚麼;
再把first_name的label改為Nickname,
還有把username的field argument加上
disabled=True,防止用戶自行修改,
最重要就是在Meta說明哪些fields需要被顯示出來。
4.
[Create template]
最後建立一張template -
registration/myaccount.html
:
{% extends 'base.html' %}
{% block content %}
<form method="POST">
{% csrf_token %}
{% for field in form %}
<label for='{{ field.name }}'>{{ field.label_tag }}</label>:
{% if field.name != "password" %}
{{ field }}
{% endif %}
{% if field.help_text %}
{{ field.help_text | safe }}
{% endif %}
{% endfor %}
<input type='submit' value='Submit'>
</form>
{% endblock %}
留意上面,form的內容加上了一行condition。
當for loop遇到password field時,我們故意把它不顯示出來。
因為Django 的password並不是普通地儲存在database,而是用hasher等加密了。
如果不遮罩,password field的內容會顯示出貌似:pbkdf2_xxxxxxxxxxxxxxxxxxxxxxx
而且即使用戶在這頁面改密碼自行更改,Django也不會接納。
所以乾脆把password field隱藏,然後在help text加上password_change的連結。
因為helptext是一串plain text,要把step 3的連結有效顯示,該用戶點擊,就要在help_text加上
safe的filter tag。
這樣就可以變成: