Bootstrap

django 实战(10): 自定义Teacher模型(与User模型一对一关系)

有了第9节,本节的工作就相对简单多了。这是因为模板文件中我们提前考虑了不同应用情况下的代码。这里我们仅仅需要重复第9节的步骤即可。

1 创建应用teacher

(base) xxx@xxx-virtual-machine:~/AuthDemo$ django-admin startapp teacher

(base) xxx@xxx-virtual-machine:~/AuthDemo$

1.1 在应用teacher的目录下创建文件和文件夹

  1. 新建模板目录templates、templatetags,
  2. 新建文件forms.py、urls.py
  3. 在模板目录templates下,创建与应用名同名的子目录

1.2 在settings.py中注册应用

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "basedata",
    "users",

    "student",

    "teacher",

]

2 在teacher/models.py中定义老师模型Teacher

from django.db import models

# Create your models here.

from users.models import User

from basedata.models import Province, City, County, College, Major    

   

# 老师信息模型(从表)与User(主表) 一对一

#                 与College(主表) 多对一

#                 与Major(主表) 多对一

#                 与Province(主表) 多对一

#                 与City(主表) 多对一

#                 与County(主表) 多对一

class Teacher(models.Model):

  name = models.CharField(max_length=20, null=False) # 姓名

  office =  models.CharField(max_length=50, null=False) # 办公地址

  # 如果删除了User表中的信息,其关联的学生信息也会被删除

  user     = models.OneToOneField(User,on_delete=models.CASCADE) #User外键

  college  = models.ForeignKey(College,on_delete=models.CASCADE) # College外键

  major    = models.ForeignKey(Major,on_delete=models.CASCADE)   # Major外键

  province = models.ForeignKey(Province,on_delete=models.CASCADE)# Province外键

  city     = models.ForeignKey(City,on_delete=models.CASCADE)    # City外键

  county   = models.ForeignKey(County,on_delete=models.CASCADE)  # County外键

 

  # 覆盖对象对外的字符串表现形式

  def __str__(self):

    return self.name

2.1  数据迁移 

(base) xxx@xxx-virtual-machine:~/AuthDemo$ python manage.py makemigrations

Migrations for 'teacher':

  teacher/migrations/0001_initial.py

    - Create model Teacher

(base) xxx@xxx-virtual-machine:~/AuthDemo$ python manage.py migrate teacher

Operations to perform:

  Apply all migrations: teacher

Running migrations:

  Applying teacher.0001_initial... OK

(base) xxx@xxx-virtual-machine:~/AuthDemo$

3 在tecaher/forms.py中,定义表单 

TecacherAddForm、TeacherUpdateForm两张表单一摸一样,只是字段名称不一样。这是因为这两张表单要用在同一个页面中。所以每个输入框的id不能一样。TeacherUpdateForm在每个字段名称后面加上了U,并且增加了一个字段passwordU,用于修改用户密码。

两张表单都综合了User模型、Teacher模型中的信息。详细代码如下:

 from django import forms

class TeacherAddForm(forms.Form):

  GENDER_TYPE = (

    ("1","男"),

    ("2","女")

  )

  picture = forms.fields.ImageField(

    label='设置头像',

    required=False,

  )

  name = forms.fields.CharField(       #要填入Student、Teacher模型中的字段

    label = '真实姓名',

    required =True,

    min_length = 2,

    max_length = 20,

    error_messages={

      "required":"真实姓名不可以为空!",

      "min_length":"真实姓名不能低于2位!",

      "max_length":"真实姓名不能超过50位!"

    }

  )

  gender = forms.ChoiceField(

    label = '性别',

    required = True,

    choices = GENDER_TYPE,

  )

  age = forms.fields.IntegerField(

    label = '年龄',

    required=True,

    min_value =10,

    max_value =120,

    error_messages={

      "required":"年龄不可以为空!",

    }

  )

  address = forms.fields.CharField(

    label = '住址',

    required=True,

    max_length=100,

    error_messages={

      "required":"住址不可以为空!",

    }

  )

  office = forms.fields.CharField(

    label = '办公地址',

    required=True,

    max_length=50,

    error_messages={

      "required":"办公地址不可以为空!",

    }

  )

  email = forms.fields.EmailField(

    label = '电子邮件',

    required=True,

    error_messages={

      "required":"电子邮件不可以为空!",

    }

  )

  phone = forms.fields.CharField(

    label = '电话',

    required=True,

    max_length=11,

    error_messages={

      "required":"电话不可以为空!",

    }

  )

  card_id = forms.fields.CharField(

    label = '身份证号码',

    required = True,

    max_length=18,

    error_messages={

      "required":"身份证号码不可以为空!",

    }

  )

  college = forms.fields.ChoiceField( #要填入Student、Teacher模型中的字段

    label = '所属学院',

  )

  major = forms.fields.ChoiceField( #要填入Student、Teacher模型中的字段

    label = '专业',

  )

  province = forms.fields.ChoiceField(  #要填入Student、Teacher模型中的字段

    label = '省份',

  )

  city = forms.fields.ChoiceField( #要填入Student、Teacher模型中的字段

    label = '地市',

  )

  county = forms.fields.ChoiceField( #要填入Student、Teacher模型中的字段

    label = '县区',

  )

  username = forms.fields.CharField(

    label = '用户名',

    required=True,

    min_length=3,

    max_length=50,

    error_messages={

      "required":"用户名不可以为空!",

      "min_length":"用户名不能低于3位!",

      "max_length":"用户名不能超过50位!"

    }

  )

  nickname = forms.fields.CharField(

    label = '昵称',

    max_length=50

  )

  '''password = forms.fields.CharField(

    label = '密码',

    required=True,

    initial='123456',

    widget=forms.PasswordInput(),

    min_length=6,

    max_length=50,

    error_messages={

      "required":"密码不可以空",

      "min_length": "密码不能低于6位!",

      "max_length": "密码不能超过50位!"

    }

  )'''

  is_active = forms.fields.BooleanField(

    label = '激活状态',

    required=False,

  )

  is_superuser= forms.fields.BooleanField(

    label = '是否是超级用户',

    required=False,

  )

class TeacherUpdateForm(forms.Form):

  GENDER_TYPE = (

    ("1","男"),

    ("2","女")

  )

  pictureU = forms.fields.ImageField(

    label='设置头像',

    required=False,

  )

  nameU = forms.fields.CharField(       #要填入Student、Teacher模型中的字段

    label = '真实姓名',

    required =True,

    min_length = 2,

    max_length = 50,

    error_messages={

      "required":"真实姓名不可以为空!",

      "min_length":"真实姓名不能低于2位!",

      "max_length":"真实姓名不能超过50位!"

    }

  )

  genderU = forms.ChoiceField(

    label = '性别',

    required = True,

    choices = GENDER_TYPE,

  )

  ageU = forms.fields.IntegerField(

    label = '年龄',

    required=True,

    min_value =10,

    max_value =120,

    error_messages={

      "required":"年龄不可以为空!",

    }

  )

  addressU = forms.fields.CharField(

    label = '住址',

    required=True,

    max_length=100,

    error_messages={

      "required":"住址不可以为空!",

    }

  )

  officeU = forms.fields.CharField(

    label = '办公地址',

    required=True,

    max_length=50,

    error_messages={

      "required":"办公地址不可以为空!",

    }

  )

  emailU = forms.fields.EmailField(

    label = '电子邮件',

    required=True,

    error_messages={

      "required":"电子邮件不可以为空!",

    }

  )

  phoneU = forms.fields.CharField(

    label = '电话',

    required=True,

    max_length=11,

    error_messages={

      "required":"电话不可以为空!",

    }

  )

  card_idU = forms.fields.CharField(

    label = '身份证号码',

    required = True,

    max_length=18,

    error_messages={

      "required":"身份证号码不可以为空!",

    }

  )

  collegeU = forms.fields.ChoiceField( #要填入Student、Teacher模型中的字段

    label = '所属学院',

  )

  majorU = forms.fields.ChoiceField( #要填入Student、Teacher模型中的字段

    label = '专业',

  )

  provinceU = forms.fields.ChoiceField(  #要填入Student、Teacher模型中的字段

    label = '省份',

  )

  cityU = forms.fields.ChoiceField( #要填入Student、Teacher模型中的字段

    label = '地市',

  )

  countyU = forms.fields.ChoiceField( #要填入Student、Teacher模型中的字段

    label = '县区',

  )

  usernameU = forms.fields.CharField(

    label = '用户名',

    required=True,

    min_length=3,

    max_length=50,

    error_messages={

      "required":"用户名不可以为空!",

      "min_length":"用户名不能低于3位!",

      "max_length":"用户名不能超过50位!"

    }

  )

  nicknameU = forms.fields.CharField(

    label = '昵称',

    max_length=50

  )

  passwordU = forms.fields.CharField(

    label = '密码',

    required=True,

    initial='123456',

    widget=forms.PasswordInput(),

    min_length=6,

    max_length=50,

    error_messages={

      "required":"密码不可以空",

      "min_length": "密码不能低于6位!",

      "max_length": "密码不能超过50位!"

    }

  )

  is_activeU = forms.fields.BooleanField(

    label = '激活状态',

    required=False,

  )

  is_superuserU= forms.fields.BooleanField(

    label = '是否是超级用户',

    required=False,

  )

  last_loginU = forms.DateTimeInput() 

4 在teacher/views.py中定义视图函数query、add、update、delete

add()、update()函数存在添加,修改失败的问题;需要将信息传递到上述两个函数重定向到的query()函数中。详细说明在代码中有:

重定向传值是通过session来进行的。

详细代码如下:

 

 

from django.shortcuts import render,redirect

# Create your views here.

from teacher.forms import TeacherAddForm,TeacherUpdateForm

from teacher.models import Teacher

from basedata.models import Province,City,County,College,Major

from django.core.paginator import Paginator

from django.core import serializers

# 导入获取自定义User对象的方法

from django.contrib.auth import get_user_model

User = get_user_model()     # 获取自定义User模型

##########################################

# User 、Teacher的操作 (一对一)            #

##########################################

# 分页查询所有指向数据库操作的主页面teacher.html

def query(request,per_page='2',current_page='1'):

  '''

  功  能:分页函数。

        若是查询所有,进行分页。若是模糊查询,不分页。

  参  数: per_page:  每页记录数, 默认值是'2'.

         current_page: 当前页, 默认值是'1', 即首页

         request.POST[name]: 模糊查询的条件

  返回值: 一个字典。字典中的key含义如下:

         'error': 可能的错误信息。    string

         'count': 查询结果总记录数。      int

         'count_all': 数据表中总记录数。   int

            若count == count_all,就是全部查询

         'per_page': 每页记录数。      int

         'num_pages': 页面总数。      int

         'current_page': 当前页面数。  int

         'page_bar_range': 分页栏显示的数字范围。list

         'result': 若不是模糊查询, 当前页面数对应的结果。querySet

                   若是模糊查询,不分页。模糊查询的所有结果。querySet

         'user_list': 'result'中逐条对应的User详情。JSON字符串。

         'form': Teacher的新增表单

         'formU': Teacher的修改表单

         'name': 查询条件。供页面刷新时<input>显示最近一次输入值用

  '''

 

  # 初始化返回值为空的字典

  context = {}

  # 读取add函数设置的session,若没有,则为None

  context['add_code'] = request.session.get('add_code')

  context['add_message'] = request.session.get('add_message')

  context['add_errorINFO'] = request.session.get('add_errorINFO')

  # 读取update设置传过来的session

  context['update_code'] = request.session.get('update_code')

  context['update_message'] = request.session.get('update_message')

  context['update_errorINFO'] = request.session.get('update_errorINFO')

  #删除由add函数设置的session

  if request.session.get('add_code') != None:

    request.session.pop('add_code')

    request.session.pop('add_message')

    request.session.pop('add_errorINFO')

  #删除由update函数设置的session

  if request.session.get('update_code') != None:

    request.session.pop('update_code')

    request.session.pop('update_message')

    request.session.pop('update_errorINFO')

 

  #因为前端上传的值都是字符串,所以需要转换为整数

  per_page=int(per_page)

  current_page=int(current_page)

  # 获取数据,按id排序

   

  varList = ''

       

  if request.POST : # 模糊查询

    varList = Teacher.objects.filter(name__contains = request.POST['search_name']).order_by('id')

    # 保存查询条件,供index.html页面要设置搜索栏表单中的相应输入框value属性用

    context['search_name'] = request.POST['search_name']

  else:  # 查询所有

    context['search_name'] = ''

    varList = Teacher.objects.all().order_by('id')

   

  if varList.exists() : # 判断查询集中是否有数据

    error = ''

  else:

    error = "表中没有数据 !"

   

  # 创建分页器实例

  paginator = Paginator(varList,per_page)

  page = paginator.get_page(current_page)

   

  # 设置在前端分页器栏为Bootstrap分页器内固定显示7页的参数

  page_bar_range = range(1,8)

  if paginator.num_pages > 7:

    if current_page-3 < 1:

        #当页面输入前三页时,使其分页器只显示前7页不变

      page_bar_range = range(1,8)

    elif current_page+3 > paginator.num_pages:

      #当页面处于最后三页时,使分页器只显示最后固定7页

      page_bar_range = range(paginator.num_pages-6,paginator.num_pages+1)

    else:

      page_bar_range = range(current_page-3,current_page+4)

  else:

    page_bar_range = paginator.page_range

  context['page_bar_range'] = page_bar_range

  context['count'] = paginator.count

  context['count_all'] = Teacher.objects.all().count();

  context['per_page'] = per_page

  context['current_page'] = current_page

  context['num_pages'] = paginator.num_pages

  context['error'] = error

 

  # 如果不是模糊查询,则分页;如果是,则不分页

  if context['count'] == context['count_all']:

    context['result'] = page

  else:

    context['result'] = varList

 

  '''

  通过Teacher(从表)正向查询的User(主表)

  一对一关系:

  只有从表的单个实例,才能通过 从表模型对象实例.外键名 进行跨表正向查询

  context['result']是一个QuerySet,不能通过上述方法反查,所以需要逐条反查。

  '''

  userList=[]

  for var in context['result']:

    userList.append( var.user) # user为从表名的外键

  '''

  转换为JSON格式的字符串, 是因为需要在前端JS中处理这个数据。

  '''

  context['user_list'] = serializers.serialize("json",userList)

  #print( context['user_list']) #在控制台显示转换后的JSON字符串

  '''

  将所有有关修改、新增所需要的基础数据列表传入前端

  供前端<select>的<option>选项使用

  '''

  context['provinceList'] = querySetToList(Province.objects.all().order_by('id').values('id','name'))

  context['cityList'] =  querySetToList(City.objects.all().order_by('id').values('id','name','province_id'))

  context['countyList'] =  querySetToList(County.objects.all().order_by('id').values('id','name','city_id'))

  context['collegeList'] =  querySetToList(College.objects.all().order_by('id').values('id','name'))

  context['majorList'] =  querySetToList(Major.objects.all().order_by('id').values('id','name','college_id'))

  context['form'] = TeacherAddForm()  #修改和新增用户用到的空表格

  #print(context['form'])

  context['formU'] = TeacherUpdateForm()  #修改和新增用户用到的空表格

  return render(request, "teacher.html", context)

def querySetToList(querySet):

  '''将querySet对象,转化为列表对象'''

  varList =[]

  for var in querySet:

     varList.append( var)

  return varList

import os

from django.conf import settings

def add(request):

  # 初始化返回信息

  # 因为add返回的是重定向到query

  # 而新增过程中会出错,所以需要向query传递信息

  # 只能通过session来query传递信息

  request.session['add_code'] = 400

  request.session['add_message'] = '用户注册失败'

  request.session['add_errorINFO'] = ''

  # 1 获取数据

  name = request.POST["name"]

  username = request.POST["username"]

  #password = request.POST["password"]

  email = request.POST["email"]

  nickname = request.POST["nickname"]

  gender = request.POST["gender"]

  age = request.POST["age"]

  phone = request.POST["phone"]

  card_id =request.POST["card_id"]

  address =request.POST["address"]

  office =request.POST["office"]

  #picture =request.POST["picture"]

  if (request.POST.get('is_active') == 'on'):

    is_active = True

  else:

    is_active = False

  if (request.POST.get('is_superuser') == 'on'):

    is_superuser = True

  else:

    is_superuser = False

  college = request.POST["college"]

  major = request.POST["major"]

  province = request.POST["province"]

  city = request.POST["city"]

  county = request.POST["county"]

   

  username_exists = User.objects.filter(username=username).exists()

  # 如果用户名已经存在

  if username_exists:

    request.session['add_errorINFO'] =  "您输入的用户名已存在!"

    print("您输入的用户名已存在!")

    return redirect("/teacher/query")

  # 如果邮箱名已经存在

  email_exists = User.objects.filter(email=email).exists()

  if email_exists:

    request.session['add_errorINFO'] = "您输入的邮箱已存在!"

    print("您输入的邮箱名已存在!")

    return redirect("/teacher/query")

  # 如果电话号码已经存在

  phone_exists = User.objects.filter(phone=phone).exists()

  if phone_exists:

    request.session['add_errorINFO'] = "您输入的电话号码已存在!"

    print("您输入的电话号码已存在!")

    return redirect("/teacher/query")

  # 如果份证号码已经存在

  card_id_exists = User.objects.filter(card_id=card_id).exists()

  if card_id_exists:

    request.session['add_errorINFO'] = "您输入的身份证号码已存在!"

    print("您输入的身份证号码已存在!")

    return redirect("/teacher/query")

  #注册通过,返回页面

  print("注册通过")

  print(request.POST.get('picture'))

  # 如果前端未选择头像文件,request.POST['picture']不存在

  # 所以需要 request.POST.get('picture')来判断

  if request.POST.get('picture') != '':   # 如果不为空,则上传图像

    # 处理图像上传

    # 获取一个文件管理器对象

    file = request.FILES['picture']

    # 获取图像文件后缀

    suffix = os.path.splitext(file.name)[-1]

    # 保存文件名为用户名+后缀

    save_file_name = username + suffix

    # 头像文件的地址和文件名称

    '''

    static 是settings.py中定义的别名,指向静态目录statics

    images 是 静态目录statics下的子目录

    user_pictures 是 用来保存用户头像的子目录

    '''

    where = '/static/images/user_pictures/'+ save_file_name

    # 分块保存image

    content = file.chunks()

    with open(where, 'wb') as f:

      for i in content:

        f.write(i)

  else:      # 如果为空,则采用默认图像

    where = '/static/images/user_pictures/'+ 'default.jpg'

  # 创建主表User

  User.objects.create_user(

    username=username,

    password='123456',  #初始密码为123456

    email=email,

    nickname = nickname,

    gender =gender,

    age =age,

    phone =phone,

    card_id =card_id,

    address =address,

    #picture=picture,

    picture = where, #将上传文件的保存路径、文件名存入数据库

    is_active =is_active,

    is_staff = True,

    is_superuser =is_superuser

  )

  request.session['add_code'] = 200

  request.session['add_message'] = '注册通过'

  # 创建从表Teacher

  user_id = User.objects.get(email = email).id

  Teacher(

    name = name,

    office =office,

    user_id = user_id,

    college_id =college,

    major_id = major,

    province_id =province,

    city_id =city,

    county_id = county,

    ).save()

  return redirect("/teacher/query")

def update(request):

  # 初始化返回信息

  # 因为update返回的是重定向到query

  # 而新增过程中会出错,所以需要向query传递信息

  # 只能通过session来query传递信息

  request.session['update_code'] = 400

  request.session['update_message'] = '用户修改失败'

  request.session['update_errorINFO'] = ''

  # 1 获取数据

  print(request.POST)

  id = request.POST['id']

  user_id =request.POST['user_id']

  name = request.POST["nameU"]

  username = request.POST["usernameU"]

  password = request.POST["passwordU"]

  email = request.POST["emailU"]

  nickname = request.POST["nicknameU"]

  gender = request.POST["genderU"]

  age = request.POST["ageU"]

  phone = request.POST["phoneU"]

  card_id =request.POST["card_idU"]

  address =request.POST["addressU"]

  office =request.POST["officeU"]

 

  if (request.POST.get('is_activeU') == 'on'):

    is_active = True

  else:

    is_active = False

  if (request.POST.get('is_superuserU') == 'on'):

    is_superuser = True

  else:

    is_superuser = False

  college = request.POST["collegeU"]

  major = request.POST["majorU"]

  province = request.POST["provinceU"]

  city = request.POST["cityU"]

  county = request.POST["countyU"]

  # 根据user_id 查询 修改前的User信息

  userBeforUpdate = User.objects.get(id = user_id)

  print(userBeforUpdate)

   

  username_exists = User.objects.filter(username=username).exists()

  # 如果用户名已经存在,并且与修改前的不一致

  if (username_exists) and (username !=userBeforUpdate.username ):

    request.session['update_errorINFO'] =  "您输入的用户名已存在!"

    print("您输入的用户名已存在!")

    return redirect("/teacher/query")

  # 如果邮箱名已经存在,并且与修改前的不一致

  email_exists = User.objects.filter(email=email).exists()

  if (email_exists) and (email !=userBeforUpdate.email ):

    request.session['update_errorINFO'] = "您输入的邮箱已存在!"

    print("您输入的邮箱名已存在!")

    return redirect("/teacher/query")

  # 如果电话号码已经存在,并且与修改前的不一致

  phone_exists = User.objects.filter(phone=phone).exists()

  if (phone_exists) and (phone !=userBeforUpdate.phone ):

    request.session['update_errorINFO'] = "您输入的电话号码已存在!"

    print("您输入的电话号码已存在!")

    return redirect("/teacher/query")

  # 如果身份证号码已经存在,并且与修改前的不一致

  card_id_exists = User.objects.filter(card_id=card_id).exists()

  if (card_id_exists) and (card_id != userBeforUpdate.card_id ):

    request.session['update_errorINFO'] = "您输入的身份证号码已存在!"

    print("您输入的身份证号码已存在!")

    return redirect("/teacher/query")

  #验证通过

  print("验证通过")

  print(request.POST.get('picture'))

  # 如果前端未选择头像文件,request.POST['picture']不存在

  # 所以需要 request.POST.get('picture')来判断

  if request.POST.get('pictureU') != '':   # 如果不为空,则上传图像

    # 处理图像上传

    # 获取一个文件管理器对象

    file = request.FILES['pictureU']

    # 获取图像文件后缀

    suffix = os.path.splitext(file.name)[-1]

    # 保存文件名为用户名+后缀

    save_file_name = username + suffix

    # 头像文件的地址和文件名称

    '''

    static 是settings.py中定义的别名,指向静态目录statics

    images 是 静态目录statics下的子目录

    user_pictures 是 用来保存用户头像的子目录

    '''

    where = '/static/images/user_pictures/'+ save_file_name

    # 分块保存image

    content = file.chunks()

    with open(where, 'wb') as f:

      for i in content:

        f.write(i)

  else:      # 如果为空,则采用以前的图像

    where = userBeforUpdate.picture

  # 修改主表User

  User.objects.filter(id= user_id).update(

    username=username,

    password=password,  

    email=email,

    nickname = nickname,

    gender =gender,

    age =age,

    phone =phone,

    card_id =card_id,

    address =address,

    #picture=picture,

    picture = where, #将上传文件的保存路径、文件名存入数据库

    is_active =is_active,

    is_staff = True,

    is_superuser =is_superuser

  )

  request.session['update_code'] = 200

  request.session['update_message'] = '修改完成'

  # 修改从表Teacher

  Teacher.objects.filter(id=id).update(

    name = name,

    office = office,

    user_id = user_id,

    college_id =college,

    major_id = major,

    province_id =province,

    city_id =city,

    county_id = county,

    )

  return redirect("/teacher/query")

def delete(request):

  # 删除所选择的记录

  # 将前端传过来的idList字符串分割为数组

  idList=request.POST['idList'].split(' ')

  user_idList=request.POST['user_idList'].split(' ')

  print(idList)

  print(user_idList)

  for var in idList: # 删除从表

    Teacher.objects.filter(id=int(var)).delete()

  for var in user_idList: #删除主表,也可以直接删除主表就级联删除从表

    User.objects.filter(id=int(var)).delete()

  return redirect("/teacher/query")

from django.shortcuts import render,redirect

# Create your views here.

from teacher.forms import TeacherAddForm,TeacherUpdateForm

from teacher.models import Teacher

from basedata.models import Province,City,County,College,Major

from django.core.paginator import Paginator

from django.core import serializers

# 导入获取自定义User对象的方法

from django.contrib.auth import get_user_model

User = get_user_model()     # 获取自定义User模型

##########################################

# User Teacher的操作 (一对一)            #

##########################################

# 分页查询所有指向数据库操作的主页面teacher.html

def query(request,per_page='2',current_page='1'):

  '''

   能:分页函数。

        若是查询所有,进行分页。若是模糊查询,不分页。

   : per_page:  每页记录数, 默认值是'2'.

         current_page: 当前页, 默认值是'1', 即首页

         request.POST[name]: 模糊查询的条件

  返回值: 一个字典。字典中的key含义如下:

         'count':         查询结果总记录数。      int

         'count_all':     数据表中总记录数。   int

            count == count_all,就是全部查询

         'per_page':      每页记录数。      int

         'num_pages':     页面总数。      int

         'current_page':  当前页面数。  int

         'page_bar_range':分页栏显示的数字范围。list

         'result':        若不是模糊查询, 当前页面数对应的结果。querySet

                          若是模糊查询,不分页,模糊查询的所有结果。querySet

         'user_list':     result对应的, Teacher模型一对一关系的User模型

         'provinceList':  省份的列表。模型Province

         'cityList':      地市的列表。模型City,Province是多对一的关系

         'countyList':    县区的列表。模型County,City是多对一的关系

         'collegeList':   学院的列表。模型College

         'majorList':     专业列表。  模型Major,College是多对一的关系

         'form':          Teacher的新增表单(包含TeacherUser的全部信息)

         'formU':         Teacher的修改表单(包含TeacherUser的全部信息)

         'search_name':   查询条件。供页面刷新时<input>显示最近一次输入值用

         'app_name':      应用名称。

         'id':            页面选择状态

         'code':          add()update()函数的状态码

         'message':       add()update()函数的信息

         'errorInfo':     add()update()函数的错误原因

  '''

 

  # 初始化返回值为空的字典

  context = {}

  # 初始化返回值为空的字典

  context = {}

  # 读取add()update()函数设置的session,若没有,则为None

  context['code']      = request.session.get('code')

  context['message']   = request.session.get('message')

  context['errorINFO'] = request.session.get('errorINFO')

  #删除由add()update()函数设置的session

  if request.session.get('code') != None:

    request.session.pop('code')

    request.session.pop('message')

    request.session.pop('errorINFO')

  # 读取update()函数设置的页面,若没有,则为None

  id ='0' #应为路由不传id值,所以设置一个

  if request.session.get('per_page') != None:

    # 读取session

    per_page     = int(request.session['per_page'])

    current_page = int(request.session['current_page'])

    id           = int(request.session['id'])

    #删除session

    request.session.pop('per_page')

    request.session.pop('current_page')

    request.session.pop('id')

  else: #没有session,则是路由传值,或初始值

    #因为路由传值、初始值都是字符串,所以需要转换为整数

    per_page     = int(per_page)

    current_page = int(current_page)

    id           = int(id)

  # 获取数据,按id排序

  varList = ''

  if request.POST : # 模糊查询

    varList = Teacher.objects.filter(name__contains = request.POST['search_name']).order_by('id')

    # 保存查询条件,供index.html页面要设置搜索栏表单中的相应输入框value属性用

    context['search_name'] = request.POST['search_name']

  else:  # 查询所有

    context['search_name'] = ''

    varList = Teacher.objects.all().order_by('id')

   

  # 创建分页器实例

  paginator = Paginator(varList,per_page)

  page = paginator.get_page(current_page)

   

  # 设置在前端分页器栏为Bootstrap分页器内固定显示7页的参数

  page_bar_range = range(1,8)

  if paginator.num_pages > 7:

    if current_page-3 < 1:

        #当页面输入前三页时,使其分页器只显示前7页不变

      page_bar_range = range(1,8)

    elif current_page+3 > paginator.num_pages:

      #当页面处于最后三页时,使分页器只显示最后固定7

      page_bar_range = range(paginator.num_pages-6,paginator.num_pages+1)

    else:

      page_bar_range = range(current_page-3,current_page+4)

  else:

    page_bar_range = paginator.page_range

  context['page_bar_range'] = page_bar_range

  context['count']          = paginator.count

  context['count_all']      = Teacher.objects.all().count();

  context['per_page']       = per_page

  context['current_page']   = current_page

  context['num_pages']      = paginator.num_pages

  context['id']             = id

 

  # 如果不是模糊查询,则分页;如果是,则不分页

  if context['count'] == context['count_all']:

    context['result'] = page

  else:

    context['result'] = varList

 

  '''

  通过Teacher(从表)正向查询的User(主表)

  一对一关系:

  只有从表的单个实例,才能通过 从表模型对象实例.外键名 进行跨表正向查询

  context['result']是一个QuerySet,不能通过上述方法反查,所以需要逐条反查。

  '''

  userList=[]

  for var in context['result']:

    userList.append( var.user) # user为从表名的外键

  '''

  转换为JSON格式的字符串, 是因为需要在前端JS中处理这个数据。

  '''

  context['user_list'] = serializers.serialize("json",userList)

  #print( context['user_list']) #在控制台显示转换后的JSON字符串

  '''

  将所有有关修改、新增所需要的基础数据列表传入前端

  供前端<select><option>选项使用

  '''

  context['provinceList'] = querySetToList(Province.objects.all().order_by('id').values('id','name'))

  context['cityList']     = querySetToList(City.objects.all().order_by('id').values('id','name','province_id'))

  context['countyList']   = querySetToList(County.objects.all().order_by('id').values('id','name','city_id'))

  context['collegeList']  = querySetToList(College.objects.all().order_by('id').values('id','name'))

  context['majorList']    = querySetToList(Major.objects.all().order_by('id').values('id','name','college_id'))

  context['form']     = TeacherAddForm()  #修改和新增用户用到的空表格

  context['formU']    = TeacherUpdateForm()  #修改和新增用户用到的空表格

  context['app_name'] ='teacher'

  return render(request, "index.html", context)

def querySetToList(querySet):

  '''querySet对象,转化为列表对象'''

  varList =[]

  for var in querySet:

     varList.append( var)

  return varList

import os

from django.conf import settings

def add(request):

  # 初始化返回信息

  # 因为add返回的是重定向到query

  # 而新增过程中会出错,所以需要向query传递信息

  # 只能通过sessionquery传递信息

  request.session['code'] = 400

  request.session['message'] = '用户注册失败'

  request.session['errorINFO'] = ''

  # 1 获取数据

  name = request.POST["name"]

  username = request.POST["username"]

  email    = request.POST["email"]

  nickname = request.POST["nickname"]

  gender   = request.POST["gender"]

  age      = request.POST["age"]

  phone    = request.POST["phone"]

  card_id  = request.POST["card_id"]

  address  = request.POST["address"]

  office   = request.POST["office"]

  if (request.POST.get('is_superuser') == 'on'):

    is_superuser = True

  else:

    is_superuser = False

  college  = request.POST["college"]

  major    = request.POST["major"]

  province = request.POST["province"]

  city     = request.POST["city"]

  county   = request.POST["county"]

  # 2 判断用户名是否已经存在  

  username_exists = User.objects.filter(username=username).exists()

  if username_exists: # 如果用户名已经存在

    request.session['errorINFO'] =  "您输入的用户名已存在!"

    return redirect("/teacher/query")

  # 3 判断邮箱名是否已经存在

  email_exists = User.objects.filter(email=email).exists()

  if email_exists: # 如果邮箱名已经存在

    request.session['errorINFO'] = "您输入的邮箱已存在!"

    return redirect("/teacher/query")

  # 4 判断电话号码是否已经存在

  phone_exists = User.objects.filter(phone=phone).exists()

  if phone_exists: # 如果电话号码已经存在

    request.session['errorINFO'] = "您输入的电话号码已存在!"

    return redirect("/teacher/query")

  # 5 判断身份证号码是否已经存在

  card_id_exists = User.objects.filter(card_id=card_id).exists()

  if card_id_exists: # 如果份证号码已经存在

    request.session['errorINFO'] = "您输入的身份证号码已存在!"

    return redirect("/teacher/query")

  # 6 判断前端是否选择头像文件

  #   如果前端未选择头像文件,request.POST['picture']不存在

  #   所以需要 request.POST.get('picture')来判断

  if request.POST.get('picture') != '':   # 如果不为空,则上传图像

    # 处理图像上传

    # 获取一个文件管理器对象

    file = request.FILES['picture']

    # 获取图像文件后缀

    suffix = os.path.splitext(file.name)[-1]

    # 保存文件名为用户名+后缀

    save_file_name = username + suffix

    # 头像文件的地址和文件名称

    '''

    static settings.py中定义的别名,指向静态目录statics

    images 静态目录statics下的子目录

    user_pictures 用来保存用户头像的子目录

    '''

    where = '/static/images/user_pictures/'+ save_file_name

    # 分块保存image

    content = file.chunks()

    with open(where, 'wb') as f:

      for i in content:

        f.write(i)

  else:      # 如果为空,则采用默认图像

    where = '/static/images/user_pictures/'+ 'default.jpg'

  # 7 创建主表User

  User.objects.create_user(

    username=username,

    password     = '123456',  #初始密码为123456

    email        = email,

    nickname     = nickname,

    gender       = gender,

    age          = age,

    phone        = phone,

    card_id      = card_id,

    address      = address,

    picture      = where, #将上传文件的保存路径、文件名存入数据库

    is_active    = True,

    is_staff     = False,

    is_superuser =is_superuser

  )

  # 8 创建从表Teacher

  user_id = User.objects.get(email = email).id

  Teacher(

    name = name,

    office =office,

    user_id = user_id,

    college_id =college,

    major_id = major,

    province_id =province,

    city_id =city,

    county_id = county,

    ).save()

  # 9 设置session

  # 操作状态

  request.session['code']    = 200

  request.session['message'] = '注册通过'

  return redirect("/teacher/query")

def update(request):

  # 初始化返回信息

  # 因为update返回的是重定向到query

  # 而新增过程中会出错,所以需要向query传递信息

  # 只能通过sessionquery传递信息

  request.session['code'] = 400

  request.session['message'] = '用户修改失败'

  request.session['errorINFO'] = ''

  # 1 获取数据

  print(request.POST)

  id = request.POST['id']

  user_id =request.POST['user_id']

  current_page = request.POST["current_page"]

  per_page     = request.POST["per_page"]

  name = request.POST["nameU"]

  username = request.POST["usernameU"]

  password = request.POST["passwordU"]

  email = request.POST["emailU"]

  nickname = request.POST["nicknameU"]

  gender = request.POST["genderU"]

  age = request.POST["ageU"]

  phone = request.POST["phoneU"]

  card_id =request.POST["card_idU"]

  address =request.POST["addressU"]

  office =request.POST["officeU"]

  if (request.POST.get('is_activeU') == 'on'):

    is_active = True

  else:

    is_active = False

  if (request.POST.get('is_superuserU') == 'on'):

    is_superuser = True

  else:

    is_superuser = False

  college = request.POST["collegeU"]

  major = request.POST["majorU"]

  province = request.POST["provinceU"]

  city = request.POST["cityU"]

  county = request.POST["countyU"]

  # 2 根据user_id 查询 修改前的User信息

  userBeforUpdate = User.objects.get(id = user_id)

  # 3 判断用户名是否已经存在  

  username_exists = User.objects.filter(username=username).exists()

  if (username_exists) and (username !=userBeforUpdate.username ):# 如果用户名已经存在,并且与修改前的不一致

    request.session['errorINFO'] =  "您输入的用户名已存在!"

    return redirect("/teacher/query")

  # 4 判断邮箱名是否已经存在

  email_exists = User.objects.filter(email=email).exists()

  if (email_exists) and (email !=userBeforUpdate.email ):# 如果邮箱名已经存在,并且与修改前的不一致

    request.session['errorINFO'] = "您输入的邮箱已存在!"

    return redirect("/teacher/query")

  # 5 判断电话号码是否已经存在

  phone_exists = User.objects.filter(phone=phone).exists()

  if (phone_exists) and (phone !=userBeforUpdate.phone ):# 如果电话号码已经存在,并且与修改前的不一致

    request.session['errorINFO'] = "您输入的电话号码已存在!"

    return redirect("/teacher/query")

  # 6 判断身份证号码是否已经存在

  card_id_exists = User.objects.filter(card_id=card_id).exists()

  if (card_id_exists) and (card_id != userBeforUpdate.card_id ):# 如果身份证号码已经存在,并且与修改前的不一致

    request.session['errorINFO'] = "您输入的身份证号码已存在!"

    print("您输入的身份证号码已存在!")

    return redirect("/teacher/query")

  # 7 判断前端是否选择头像文件

  #   如果前端未选择头像文件,request.POST['picture']不存在

  #   所以需要 request.POST.get('picture')来判断

  if request.POST.get('pictureU') != '':   # 如果不为空,则上传图像

    # 处理图像上传

    # 获取一个文件管理器对象

    file = request.FILES['pictureU']

    # 获取图像文件后缀

    suffix = os.path.splitext(file.name)[-1]

    # 保存文件名为用户名+后缀

    save_file_name = username + suffix

    # 头像文件的地址和文件名称

    '''

    static settings.py中定义的别名,指向静态目录statics

    images 静态目录statics下的子目录

    user_pictures 用来保存用户头像的子目录

    '''

    where = '/static/images/user_pictures/'+ save_file_name

    # 分块保存image

    content = file.chunks()

    with open(where, 'wb') as f:

      for i in content:

        f.write(i)

  else:      # 如果为空,则采用以前的图像

    where = userBeforUpdate.picture

  # 8 修改主表User

  User.objects.filter(id= user_id).update(

    username=username,

    password=password,  

    email=email,

    nickname = nickname,

    gender =gender,

    age =age,

    phone =phone,

    card_id =card_id,

    address =address,

    #picture=picture,

    picture = where, #将上传文件的保存路径、文件名存入数据库

    is_active =is_active,

    is_staff = True,

    is_superuser =is_superuser

  )

  # 9 修改从表Teacher

  Teacher.objects.filter(id=id).update(

    name = name,

    office = office,

    user_id = user_id,

    college_id =college,

    major_id = major,

    province_id =province,

    city_id =city,

    county_id = county,

    )

  # 10 设置session

  # 操作状态

  request.session['code']    = 200

  request.session['message'] = '用户组修改成功'

  # 页面状态

  request.session['current_page'] = current_page

  request.session['per_page']     = per_page

  request.session['id']           = id

  return redirect("/teacher/query")

def delete(request):

  # 删除所选择的记录

  # 将前端传过来的idList字符串分割为数组

  idList=request.POST['idList'].split(' ')

  user_idList=request.POST['user_idList'].split(' ')

  print(idList)

  print(user_idList)

  for var in idList: # 删除从表

    Teacher.objects.filter(id=int(var)).delete()

  for var in user_idList: #删除主表,也可以直接删除主表就级联删除从表

    User.objects.filter(id=int(var)).delete()

  return redirect("/teacher/query")

5  模板文件

除了teacher/module_query.html之外,其他的都可以第9节的模板文件。

<!-- module_query.html  在应用teacher目录下的模板目录templates中的teacher子目录中

  内  容:以<table>展示查询和模糊查询的结果。

         在<table>第一列设置复选框,用于同时选择多条记录,共批量删除用。

         在<table>最后一列为操作列。设置两个默认禁用图标按钮用于当前行的修改、删除。

  涉及的变量:

    {{result}}: 含有多条查询结果。

      每一行的key和数据表中的字段名相同。若{{var}}是{{result}}的一行数据,

      那么各列数据分别为:{{var.id}}、{{var.name}}、{{var.office}}、

        {{var.college}}、{{var.major}}、{{var.province}}、

        {{var.city}}、{{var.county}}、{{var.user_id}}

-->

<table id='table' class="table table-striped text-info">

  <thead>

    <tr>

      <td><input id="allChecked" type="checkbox"></td>

      <th><h4>ID</h4></th>

      <th><h4>姓名</h4></th>

      <th><h4>办公地址</h4></th>

      <th><h4>学院</h4></th>

      <th><h4>专业</h4></th>

      <th><h4>省份</h4></th>

      <th><h4>地市</h4></th>

      <th><h4>县</h4></th>

      <th><h4>User</h4></th>

      <th><h4>操作</h4></th>

    </tr>

  </thead>

  <tbody>

    {% for var in result %}

      <tr>

        <td><input class="checked" type="checkbox"></td>

        <td class="id">       <h5>{{var.id}}</h5></td>

        <td>                  <h5>{{var.name}}</h5></td>

        <td>                  <h5>{{var.office}}</h5></td>

        <td class="college">  <h5>{{var.college}}</h5></td>

        <td class="major">    <h5>{{var.major}}</h5></td>

        <td class="province"> <h5>{{var.province}}</h5></td>

        <td class="city">     <h5>{{var.city}}</h5></td>

        <td class="county">   <h5>{{var.county}}</h5></td>

        <td class="user_id">  <h5>{{var.user_id}}</h5></td>

        <td class="edit">

          <!--修改按钮 启用id为module_updateModal的弹窗-->

          <button type="button" class="update" data-toggle="modal" data-target="#module_updateModal" disabled="disabled">

            <i class="fa-regular fa-pen-to-square"></i>

          </button>

          <!--删除按钮 启用id为module_deleteModal的弹窗-->

          <button type="button" class="delete" data-toggle="modal" data-target="#module_deleteModal" disabled="disabled">

            <i class="fa-solid fa-trash-can"></i>

          </button>

        </td>

      </tr>

    {% endfor %}

  </tbody>

</table>

 6 在teacher/urls.py中定义子路由

from django.urls import path,re_path

from . import views as view

# 子路由列表

urlpatterns = [

  path('query/', view.query,  name='query-url'),  # 分页查询

  re_path(r'^query/(?P<per_page>\d+)/(?P<current_page>\d+)/$',view.query),  #传值路由

  path('add/',   view.add,    name='add-url'),     # 新增

  path('update/',view.update, name='update-url'),  # 修改

  path('delete/',view.delete, name='delete-url'),  # 删除

]

7 在AuthDemo/urls.py中管理子路由 

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
  path("admin/", admin.site.urls),
  path('basedata/',include(('basedata.urls','basedata'))),
  path('student/', include(('student.urls', 'student'))),

  path('teacher/', include(('teacher.urls', 'teacher'))),

]

8 访问http://127.0.0.1:8000/teacher/query/ 

可以得到与第9节类似的页面

;