IT策士10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我也会在其它平台持续发布最新文章,助你少走弯路。
大家好,我是IT策士。用户登录之后,第一件想做的事往往是看看自己的账号信息,或者改个昵称、换个密码。今天我们就来打造电商平台的个人中心——让用户有一个属于自己的"小窝",并且能自助修改手机号、邮箱、密码等信息。
本篇的内容会大量复用之前注册模块里的邮箱激活逻辑,同时引入login_required装饰器来保护敏感页面。跟着我一步步来,你会发现 Django 处理这些用户操作是多么顺手。
一、需求分析
个人中心需要实现以下功能:
展示基本信息:用户名、手机号、邮箱、邮箱激活状态。
修改基本信息:允许用户更换用户名、手机号、邮箱。
修改邮箱后,邮箱激活状态重置为
False,并重新发送激活邮件。修改手机号时,需要通过短信验证码验证新手机号(暂用模拟)。
修改密码:旧密码验证通过后才能设置新密码。
权限控制:仅登录用户可访问,且只能修改自己的信息。
二、定义表单
在apps/users/forms.py中追加两个表单。
2.1 个人信息表单
class UpdateProfileForm(forms.ModelForm): class Meta: model=User fields=['username','phone','email']widgets={'username':forms.TextInput(attrs={'class':'form-control','placeholder':'用户名'}),'phone':forms.TextInput(attrs={'class':'form-control','placeholder':'手机号'}),'email':forms.EmailInput(attrs={'class':'form-control','placeholder':'邮箱'}),}def clean_phone(self): phone=self.cleaned_data.get('phone')ifphone:# 排除当前用户,检查手机号是否被其他人占用ifUser.objects.filter(phone=phone).exclude(pk=self.instance.pk).exists(): raise forms.ValidationError('该手机号已被其他用户绑定')returnphone def clean_email(self): email=self.cleaned_data.get('email')ifemail:ifUser.objects.filter(email=email).exclude(pk=self.instance.pk).exists(): raise forms.ValidationError('该邮箱已被其他用户绑定')returnemail这里使用
ModelForm直接绑定用户模型,简化字段定义。
2.2 修改密码表单
class ChangePasswordForm(forms.Form): old_password=forms.CharField(label='当前密码',widget=forms.PasswordInput(attrs={'class':'form-control','placeholder':'请输入当前密码'}))new_password=forms.CharField(label='新密码',min_length=6,max_length=20,widget=forms.PasswordInput(attrs={'class':'form-control','placeholder':'新密码(至少6位)'}))confirm_password=forms.CharField(label='确认新密码',widget=forms.PasswordInput(attrs={'class':'form-control','placeholder':'再次输入新密码'}))def clean(self): cleaned_data=super().clean()new=cleaned_data.get('new_password')confirm=cleaned_data.get('confirm_password')ifnew and confirm and new!=confirm: raise forms.ValidationError('两次新密码不一致')returncleaned_data三、编写视图
打开apps/users/views.py,在原有基础上增加以下内容。
先确保导入齐全,顶部加入:
from django.contrib.auth.decoratorsimportlogin_required from django.contrib.authimportupdate_session_auth_hash from .formsimportUpdateProfileForm, ChangePasswordFormimportrandom from django.core.mailimportsend_mail3.1 个人中心主页
@login_required(login_url='users:login')def personal_center(request):returnrender(request,'users/center.html')这个视图极为简单,只是渲染模板。模板里会直接使用
{{ user }}显示当前登录用户的信息。
3.2 修改个人信息
@login_required(login_url='users:login')def update_profile(request): user=request.userifrequest.method=='POST':form=UpdateProfileForm(request.POST,instance=user)ifform.is_valid(): old_email=user.email form.save()# 如果邮箱发生了变更,重置激活状态并发送激活邮件new_email=form.cleaned_data.get('email')ifnew_email and new_email!=old_email: user.email_active=False user.save(update_fields=['email_active'])# 生成激活 tokentoken=str(random.randint(100000,999999))request.session[f'email_token_{user.id}']=token activate_url=request.build_absolute_uri(f'/users/activate/{user.id}/{token}/')send_mail(subject='重新激活你的电商账号',message=f'你的邮箱已更新,请点击链接重新激活:{activate_url}',from_email='noreply@example.com',recipient_list=[new_email],)messages.warning(request,'邮箱已更新,请前往新邮箱查收激活邮件(终端查看)。')else: messages.success(request,'个人信息更新成功。')returnredirect('users:center')else: form=UpdateProfileForm(instance=user)returnrender(request,'users/update_profile.html',{'form':form})说明:
使用
instance=user绑定当前用户,ModelForm自动完成保存。如果修改了邮箱,程序会自动重置
email_active并发送激活邮件。激活链接沿用第 6 篇的activate_email视图,完全复用。
3.3 修改密码
@login_required(login_url='users:login')def change_password(request):ifrequest.method=='POST':form=ChangePasswordForm(request.POST)ifform.is_valid(): user=request.user old=form.cleaned_data['old_password']# 校验旧密码是否正确ifnot user.check_password(old): form.add_error('old_password','当前密码不正确')returnrender(request,'users/change_password.html',{'form':form})# 设置新密码并保存user.set_password(form.cleaned_data['new_password'])user.save()# 更新 session 认证哈希,防止密码修改后 session 失效update_session_auth_hash(request, user)messages.success(request,'密码修改成功。')returnredirect('users:center')else: form=ChangePasswordForm()returnrender(request,'users/change_password.html',{'form':form})关键点:
update_session_auth_hash的作用是,在修改密码后让当前用户的 session 继续保持有效,否则 Django 会立即将该用户登出。这一行必不可少。
3.4 短信验证码发送(复用)
我们在第 6 篇已经写了send_sms_code视图,个人中心修改手机号时也可以调用它。由于前端 AJAX 请求需要 CSRF 令牌,我们会在模板中直接复用它。
四、配置 URL
在apps/users/urls.py中添加新路由:
urlpatterns=[# ... 之前的路由 ...path('center/', views.personal_center,name='center'), path('update/', views.update_profile,name='update_profile'), path('change_password/', views.change_password,name='change_password'),]五、设计模板
5.1 个人中心主页
创建apps/users/templates/users/center.html:
{% extends'base.html'%}{% block title %}个人中心{% endblock %}{% block content %}<divclass="row"><!-- 侧边栏 --><divclass="col-md-3"><divclass="card shadow-sm"><divclass="card-header bg-primary text-white"><h5class="mb-0">个人中心</h5></div><ulclass="list-group list-group-flush"><liclass="list-group-item active"><ahref="{% url 'users:center' %}"class="text-white text-decoration-none">个人信息</a></li><liclass="list-group-item"><ahref="{% url 'users:update_profile' %}"class="text-decoration-none">修改资料</a></li><liclass="list-group-item"><ahref="{% url 'users:change_password' %}"class="text-decoration-none">修改密码</a></li><liclass="list-group-item"><ahref="#"class="text-decoration-none">收货地址</a></li><liclass="list-group-item"><ahref="#"class="text-decoration-none">我的订单</a></li></ul></div></div><!-- 主内容 --><divclass="col-md-9"><divclass="card shadow-sm"><divclass="card-header bg-light"><h4class="mb-0">👤 基本信息</h4></div><divclass="card-body"><tableclass="table table-bordered"><tr><thstyle="width:150px;">用户名</th><td>{{user.username}}</td></tr><tr><th>手机号</th><td>{{user.phone|default:"未绑定"}}</td></tr><tr><th>邮箱</th><td>{{user.email|default:"未绑定"}}</td></tr><tr><th>邮箱状态</th><td>{%ifuser.email_active %}<spanclass="badge bg-success">已激活</span>{%else%}<spanclass="badge bg-warning text-dark">未激活</span>{% endif %}</td></tr><tr><th>注册时间</th><td>{{user.date_joined|date:"Y-m-d H:i"}}</td></tr></table></div></div></div></div>{% endblock %}5.2 修改个人信息页面
创建apps/users/templates/users/update_profile.html:
{% extends'base.html'%}{% block title %}修改资料{% endblock %}{% block content %}<divclass="row justify-content-center"><divclass="col-md-6"><divclass="card shadow-sm"><divclass="card-body p-4"><h3class="text-center mb-4">✏️ 修改个人资料</h3><formmethod="post"novalidate>{% csrf_token %}<divclass="mb-3"><labelclass="form-label">用户名</label>{{form.username}}{{form.username.errors}}</div><divclass="mb-3"><labelclass="form-label">手机号</label>{{form.phone}}{{form.phone.errors}}</div><divclass="mb-3"><labelclass="form-label">邮箱</label>{{form.email}}{{form.email.errors}}</div>{%ifform.non_field_errors %}<divclass="alert alert-danger">{{form.non_field_errors.0}}</div>{% endif %}<buttontype="submit"class="btn btn-primary w-100">保存修改</button><ahref="{% url 'users:center' %}"class="btn btn-outline-secondary w-100 mt-2">取消</a></form></div></div></div></div>{% endblock %}5.3 修改密码页面
创建apps/users/templates/users/change_password.html:
{% extends'base.html'%}{% block title %}修改密码{% endblock %}{% block content %}<divclass="row justify-content-center"><divclass="col-md-6"><divclass="card shadow-sm"><divclass="card-body p-4"><h3class="text-center mb-4">🔒 修改密码</h3><formmethod="post"novalidate>{% csrf_token %}<divclass="mb-3"><labelclass="form-label">{{form.old_password.label}}</label>{{form.old_password}}{{form.old_password.errors}}</div><divclass="mb-3"><labelclass="form-label">{{form.new_password.label}}</label>{{form.new_password}}{{form.new_password.errors}}</div><divclass="mb-3"><labelclass="form-label">{{form.confirm_password.label}}</label>{{form.confirm_password}}{{form.confirm_password.errors}}</div>{%ifform.non_field_errors %}<divclass="alert alert-danger">{{form.non_field_errors.0}}</div>{% endif %}<buttontype="submit"class="btn btn-danger w-100">修改密码</button><ahref="{% url 'users:center' %}"class="btn btn-outline-secondary w-100 mt-2">取消</a></form></div></div></div></div>{% endblock %}六、更新导航栏
确保templates/base.html的导航栏中,"个人中心"链接指向{% url 'users:center' %}:
<li><aclass="dropdown-item"href="{% url 'users:center' %}">个人中心</a></li>七、完整流程测试
启动服务器:
python manage.py runserver7.1 访问个人中心
以任一用户登录,例如
13800138000。点击导航栏的用户名 → “个人中心”。
浏览器显示用户名、手机号、邮箱及激活状态、注册时间。
7.2 修改用户名
在个人中心页面点击侧边栏"修改资料"(或直接访问
/users/update/)。将用户名改为新名字,点击"保存修改"。
页面重定向回个人中心,提示"个人信息更新成功。",用户名已更新。
终端输出:
[21/May/202614:30:22]"POST /users/update/ HTTP/1.1"3020[21/May/202614:30:22]"GET /users/center/ HTTP/1.1"20036547.3 修改邮箱并重新激活
在修改资料页,将邮箱改为
newemail@example.com。提交后,页面提示"邮箱已更新,请前往新邮箱查收激活邮件(终端查看)。"
终端控制台打印激活邮件:
Content-Type: text/plain;charset="utf-8"Subject: 重新激活你的电商账号 From: noreply@example.com To: newemail@example.com 你的邮箱已更新,请点击链接重新激活:http://127.0.0.1:8000/users/activate/2/835472/- 复制链接在浏览器打开,提示"邮箱激活成功!“,个人中心中邮箱状态变为"已激活”。
控制台记录:
[21/May/202614:35:10]"POST /users/update/ HTTP/1.1"3020... MIME-Version:1.0...[21/May/202614:35:10]"GET /users/center/ HTTP/1.1"2003654[21/May/202614:37:02]"GET /users/activate/2/835472/ HTTP/1.1"30207.4 修改密码
在个人中心侧边栏点击"修改密码"。
输入当前密码、新密码(如
newpass123)、确认新密码,提交。提示"密码修改成功。",并保持在登录状态(不会掉线)。
退出后重新登录,旧密码失效,新密码生效。
验证旧密码错误的情况:
- 故意输错当前密码,提交后页面显示"当前密码不正确"。
终端输出:
[21/May/202614:40:33]"POST /users/change_password/ HTTP/1.1"2002987八、总结与下集预告
今天我们完成了用户体系的核心闭环:
搭建了个人中心展示页面,带侧边栏导航;
实现了个人信息修改,修改邮箱自动触发重新激活;
实现了安全的密码修改流程,确保旧密码校验和 session 保持;
所有视图都加上
login_required保护,未登录用户自动跳转登录页。
有了牢固的用户身份与个人资料管理基础,明天我们要进入一个更贴近电商场景的功能——收货地址管理。用户下单前必须选择地址,所以第 9 篇我们将实现地址的增删改查,并支持设置默认地址。
想了解更多还可以去其它平台搜索「IT策士」,一起升级 IT 思维 !
本文为《Django 从 0 到 1 打造完整电商平台》系列第 8 篇,作者:IT策士,未经授权禁止转载。