Django图书荐购云平台开发与实践 - 3用户信息模块

接下来开发用户信息模块,用户信息模块主要包括用户注册、用户登录、显示用户信息、用户密码的修改。
用户的注册、登陆、密码修改和找回功能在前面已经实现,只需要略做修改就可以了。参见(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个图书馆。


image.png

在个人用户修改信息的界面,可以看到有一个所属馆的下拉框,可以为个人用户添加所属馆的信息。


image.png

到这里,用户信息模块的准备工作就做的差不多了,下面开始逐一实现用户信息模块的其他功能。

会话控制

在实际使用中,还需要会话控制session。在验证登陆时,将用户名保存到session中,然后在base.html模板中做一个验证,判断用户是或否登陆,已经登陆则显示用户名和退出按钮,没有登陆的显示登陆按钮。

#base.html 的 网页头
      
      
{# 如果session中没有用户username,则显示登陆 #} {% if not request.session.username %} {% else %} {% endif %}
#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 %}
头像

个人信息
{% csrf_token %}
荐购历史
{% 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())

初步的个人信息页面展示如下:

image.png

可以看到左边这里有个头像框的预留位置,下面就要实现用户上传和修改头像的功能。

用户头像的上传与更改

要实现用户头像的上传首先需要做一些准备工作。
首先我们在重写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 %}
    
{% else %}
{% endif %}
{{ user.last_name }}
{{ user.first_name }}
...

fieldset是bootstrap4中的禁用字段集,对

添加 disabled 属性来禁用
内的所有控件。

到这里,我们实现了用户的注册、登陆、修改密码、密码找回,还实现了用户的个人信息中心,用户可以修改头像以及部分个人信息。

你可能感兴趣的:(Django图书荐购云平台开发与实践 - 3用户信息模块)