接下来开发用户信息模块,用户信息模块主要包括用户注册、用户登录、显示用户信息、用户密码的修改。
用户的注册、登陆、密码修改和找回功能在前面已经实现,只需要略做修改就可以了。参见(10) Django - Auth认证系统
由于用户信息表需要和图书馆信息表关联,这里一并要把图书馆信息表的模型给实现了。
# user 的 models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class Library(models.Model):
lib_id = models.AutoField('序号', primary_key=True)
lib_name = models.CharField('图书馆名称', max_length=50)
lib_address = models.CharField('地址', max_length=150)
lib_contact = models.CharField('联系人', max_length=20)
lib_phone = models.CharField('联系电话', max_length=20)
lib_email = models.EmailField('Email', max_length=150)
def __str__(self):
return self.lib_name
class Meta:
verbose_name = '图书馆用户'
verbose_name_plural = '图书馆用户'
class MyUser(AbstractUser):
id_number = models.CharField('学号/工号', max_length=16)
avatar = models.ImageField(upload_to='avatar', default='default.png', verbose_name="头像")
qq = models.CharField('QQ号码', max_length=16)
weChat = models.CharField('微信账号', max_length=100)
mobile = models.CharField('手机号码', max_length=11)
lib_id = models.ForeignKey(Library, on_delete=models.CASCADE, blank=True, null=True, verbose_name='所属馆')
# 设置返回值
def __str__(self):
return self.username
class Meta:
verbose_name = '个人用户'
verbose_name_plural = '个人用户'
定义好模型后,需要删除之前的全部数据表和所有app下migrations文件下的0001_initial.py
文件,删除之前将表中数据导出保存好,最后重新执行makegirations和migrate命令创建好模型和数据库表。接着需要重写user下的__init__.py
文件,以在后台显示修改后的MyUser模型。
#user 的 __init__.py
#设置App(user)的中文名
from django.apps import AppConfig
import os
# 修改app在admin后台显示名称
# default_app_config的值来自apps.py的类名
default_app_config = 'user.IndexConfig'
# 获取当前app的命名
def get_current_app_name(_file):
return os.path.split(os.path.dirname(_file))[-1]
# 重写类IndexConfig
class IndexConfig(AppConfig):
name = get_current_app_name(__file__)
verbose_name = '用户管理'
最后在user的admin.py中注册新的模型即可。
#user 的 admin.py
from django.contrib import admin
from .models import MyUser, Library
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as gl
@admin.register(MyUser)
class MyUserAdmin(UserAdmin):
list_display = ['username', 'email', 'id_number', 'mobile', 'qq', 'weChat', 'lib_id']
#修改用户时,在个人信息里添加 'id_number'、'mobile 、'qq'、'weChat'的信息录入
#将源码的UserAdmin.fieldsets转换成列表格式
fieldsets = list(UserAdmin.fieldsets)
#重写UserAdmin的fieldsets,添加'id_number'、'mobile'、'qq'、'weChat'的信息录入
fieldsets[1] = (gl('Personal info'), {'fields':('first_name','last_name','email','id_number','mobile','qq','weChat','lib_id')})
@admin.register(Library)
class LibraryAdmin(admin.ModelAdmin):
#设置显示的字段
list_display = ['lib_id','lib_name', 'lib_address', 'lib_contact', 'lib_phone', 'lib_email']
#设置搜索字段
search_fields = ['lib_id', 'lib_name']
#设置每页最大记录数
list_per_page = 20
#设置显示字段中可被链接的字段
list_display_links = ['lib_id', 'lib_name']
#设置在列表展示页面可更改的字段
# list_editable = ['cata_id']
准备工作就绪以后,打开admin后台管理,在这之前不要忘记创建superuser。可以看到后台用户管理模块里多里一个图书馆模块。点击增加图书馆,可以添加图书馆信息,这里添加了2个图书馆。
在个人用户修改信息的界面,可以看到有一个所属馆的下拉框,可以为个人用户添加所属馆的信息。
到这里,用户信息模块的准备工作就做的差不多了,下面开始逐一实现用户信息模块的其他功能。
会话控制
在实际使用中,还需要会话控制session。在验证登陆时,将用户名保存到session中,然后在base.html模板中做一个验证,判断用户是或否登陆,已经登陆则显示用户名和退出按钮,没有登陆的显示登陆按钮。
#base.html 的 网页头
#user 的views.py
def loginView(request):
#设置标题和两外两个URL链接
title = '登录'
headers = '用户登录'
unit_2 = '/user/register.html'
unit_2_name = '立即注册'
unit_1 = '/user/setpassword.html'
unit_1_name = '修改密码'
if request.method == 'POST':
#用户提交登录,获取登录的用户名和密码
username = request.POST.get('username', '')
password = request.POST.get('password', '')
#首先查找User数据表中有没有该用户,没有提示注册
if MyUser.objects.filter(username=username):
#如果有该用户,则检查用户名和密码是否正确,错误提示重新输入
user = authenticate(username=username, password=password)
if user:
if user.is_active:##判断用户是否被激活,是则由内置函数login完成登录,跳转到主页
login(request, user)
#将用户的工号/学号写入session
request.session['username'] = user.username
return redirect('/book')
else:
tips = '账号密码错误,请重新输入'
else:
tips = '用户不存在,请注册'
return render(request, 'user.html', locals())
def logoutView(request):
logout(request)#退出登录
try:
del request.session['userid']
except KeyError:
pass
return redirect('/user/login.html')#跳转到登录页面
代码和前面章节的基本相同,除了在登陆成功时将用户名写入session,以及在模板中对用户名做一个检查,最后在登出时删除session。
用户个人信息展示
这里将要实现一个个人信息展示页,其中展示了个人全部的信息,并且允许个人用户可以修改部分个人信息,如密码、联系方式等。有些信息不能修改,有些可以修改。个人信息展示比较简单,只要通过session做一个判断,如果用户登录,则从数据库获取用户的个人信息并展示给用户,如果session里没有用户登录信息,则跳转到登录页面。
下面是代码:
#定义URL
path('userinfo.html', views.userinfoView, name='userinfo'),
#userinfo.html模板
{% extends 'base.html' %}
{% load staticfiles %}
{% block search %}
{% endblock %}
{% block body %}
个人信息
荐购历史
{% endblock %}
#user 的 views.py 的 userinfoView
def userinfoView(request):
if not request.session.get('username'):
return redirect('login.html')
else:
username = request.session.get('username')
user = MyUser.objects.filter(username=username)[0]
return render(request, 'userinfo.html', locals())
初步的个人信息页面展示如下:
可以看到左边这里有个头像框的预留位置,下面就要实现用户上传和修改头像的功能。
用户头像的上传与更改
要实现用户头像的上传首先需要做一些准备工作。
首先我们在重写user模型的时候,里面要添加一个字段,用来保存用户上传图片的地址信息。
avatar = models.ImageField(upload_to='avatar', default='default.png', verbose_name="头像")
上面MyUser模型完整的代码里已经包含了avatar这个字段,其中的参数upload_to表示图片上传到哪里,default参数可选,表示默认的图片,如果没有default参数,那必须加上null=True,blank=True参数,否则在执行数据迁移时会报错。
模型中定义好字段以后,我们还需要在项目的settings.py中设置用户上传图片的目录和路径。
#settings.py
#用户上传图片的路径, 'user'是app应用名,表示图片上传到user/media/avatar下
MEDIA_ROOT = os.path.join(BASE_DIR, 'user', "media/avatar")
#MEDIA_URL是用户从前台读取图片的地址,最后的'/'不能省
MEDIA_URL = "media/avatar/"
接下来我们在user应用下新建一个forms.py,用来定义表单模型。
from django import forms
#上传头像图片的表单
class AvataruploadForm(forms.Form):
avatar = forms.ImageField()
然后到应用的urls.py里添加URL地址信息,userinfo用来展示用户信息和修改用户头像,alteruserinfo用来修改用户信息。
path('userinfo.html', views.userinfoView, name='userinfo'),
path('alteruserinfo.html', views.alterUserinfoView, name='alteruserinfo'),
接下来分别编写视图函数。
#展示个人信息页和修改头像,@login_required验证登录,未登录则跳转到登录页面
@login_required
def userinfoView(request):
#获取用户名
username= request.user.username
#获取该用户的模型实例对象
user = get_object_or_404(MyUser, username=username)
#如果是POST请求,则说明用户是在修改头像
if request.method == 'POST':
#将用户的请求信息和上传的图片文件放入自定义的表单模型中
form = AvataruploadForm(request.POST, request.FILES)
#验证表单
if form.is_valid():
#获取用户上传的文件
avatar = request.FILES['avatar']
#设置保存的文件名
imagename = get_random_str() + '.jpg'
#保存文件的路径
imagepath = os.path.join(settings.MEDIA_ROOT, imagename)
#使用pillow压缩和保存图像文件
img = Image.open(avatar)
crop_im = img.resize((300, 300), Image.ANTIALIAS)
crop_im.save(imagepath)
try:
#如果存在老的头像且不是默认头像就删除
if request.user.avatar != 'default.png':
oldimage = '%s/%s' % (settings.MEDIA_ROOT, request.user.avatar)
os.remove(oldimage)
except:
pass
#更新用户的头像在数据库中的地址
user.avatar = imagename
user.save()
#跳转到用户信息页
return redirect('userinfo.html')
return render(request, 'userinfo.html', locals())
上面的展示用户信息和修改用户头像,每一步都说明的很清楚,接下来,实现修改用户信息。
修改用户信息
首先在forms.py中定义个人信息的数据模型表单及验证函数.
from django import forms
from .models import MyUser, Library
import re
#个人信息的数据库表单
class UserinfoForm(forms.ModelForm):
#模型与表单设置
class Meta:
#绑定模型
model = MyUser
#选择转换哪些模型的字段为表单字段
fields = ['first_name','last_name','id_number','email', 'qq','weChat','mobile','lib_id']
#定义表单字段的css样式
widgets = {
'first_name': forms.widgets.TextInput(attrs={'class': 'form-control','autocomplete':'off'}),
'last_name': forms.widgets.TextInput(attrs={'class': 'form-control','autocomplete':'off'}),
'id_number': forms.widgets.TextInput(attrs={'class': 'form-control','autocomplete':'off'}),
'email': forms.widgets.EmailInput(attrs={'class': 'form-control','autocomplete':'off'}),
'qq': forms.widgets.TextInput(attrs={'class': 'form-control','autocomplete':'off'}),
'weChat': forms.widgets.TextInput(attrs={'class': 'form-control','autocomplete':'off'}),
'mobile': forms.widgets.TextInput(attrs={'class': 'form-control','autocomplete':'off'}),
}
# 函数必须以clean_开头,以模型的字段名结尾,如mobile是MyUser模型的字段
def clean_mobile(self):
"""
通过正则表达式验证手机号码是否合法
"""
mobile = self.cleaned_data['mobile']
mobile_regex = r'^1[34578]\d{9}$'
p = re.compile(mobile_regex)
if p.match(mobile):
return mobile
else:
raise forms.ValidationError('手机号码非法', code='invalid mobile')
def clean_qq(self):
qq = self.cleaned_data['qq']
if qq.isdigit():
return qq
else:
raise forms.ValidationError('QQ号码应全部为数字', code='invalid qq')
def clean_id_number(self):
id_number = self.cleaned_data['id_number']
if id_number.isdigit():
return id_number
else:
raise forms.ValidationError('学号/工号应全部为数字', code='invalid id_number')
继续编写修改个人信息的视图函数。
#修改个人信息,@login_required验证登录,未登录则跳转到登录页面
@login_required(login_url='/user/login.html')
def alterUserinfoView(request):
#获取用户名
username = request.user.username
#获取该用户的实例化模型对象
instance = MyUser.objects.get(username=username)
if instance:
#将实例化模型赋给自定义的表单模型
user = UserinfoForm(instance=instance)
#如果是POST请求,则说明用户想修改个人信息
if request.method == 'POST':
#将用户的POST请求信息和用户的实例化模型放入自定义表单中
userinfo = UserinfoForm(request.POST, instance=instance)
#验证表单,通过则保存表单数据
if userinfo.is_valid():
userinfo.save()
#跳转到用户信息展示页
return redirect('userinfo.html')
else:
#未通过验证,向用户展示错误消息
error_msg = userinfo.errors.as_json()
return render(request, 'alteruserinfo.html', locals())
到这里,用户信息模块基本上就实现的差不多了。
由于荐购系统需要实名制,因此在读者修改个人资料以后需要图书馆管理员进行后台认证,认证通过的读者才能进行荐购,而且不能再修改姓名、学号/工号和所属馆的资料。
权限的验证主要通过模板进行,如将读者分为普通读者和认证读者,认证读者有荐购权限,那么在模板中可以这样设置:
{# 对读者权限进行验证,如果有荐购权限,说明读者资料通过审核,不允许再修改姓名、工号和所属馆 #}
{% if perms.recommend.add_recommend %}
fieldset是bootstrap4中的禁用字段集,对
到这里,我们实现了用户的注册、登陆、修改密码、密码找回,还实现了用户的个人信息中心,用户可以修改头像以及部分个人信息。