1 创建应用lecture
(base) xxx@xxx-virtual-machine:~/AuthDemo$ django-admin startapp lecture
(base) xxx@xxx-virtual-machine:~/AuthDemo$
1.1 在应用lecture的目录下创建文件和文件夹
新建模板目录templates、templatetags,
新建文件forms.py、urls.py
在模板目录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",
"lecture",
]
2 在lecture/models.py中定义讲座模型Lecture
Teacher模型与Lecture模型一对多关系,外键定义在Lecture模型内
from django.db import models
# Create your models here.
from teacher.models import Teacher
# 讲座模型 与Teacher是多对一的关系
class Lecture(models.Model):
# 如果删除了老师,关联的讲座也会被删除
lectureTeacher = models.ForeignKey(Teacher,on_delete=models.CASCADE)
name = models.CharField(max_length=256, null=False) # 讲座名称
address = models.CharField(max_length=256) # 讲座地点
start_time = models.DateTimeField(null=True) # 讲座开始时间
status = models.BooleanField(default=True) # 讲座状态
limits = models.IntegerField(default=1000) # 讲座限制人数
# 覆盖对象对外的字符串表现形式
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 lecture
Operations to perform:
Apply all migrations: teacher
Running migrations:
Applying teacher.0001_initial... OK
(base) xxx@xxx-virtual-machine:~/AuthDemo$
3 在lecture/forms.py中,定义表单
LectureAddForm、LectureUpdateForm两张表单一摸一样,只是字段名称不一样。这是因为这两张表单要用在同一个页面中。所以每个输入框的id不能一样。LectureUpdateForm在每个字段名称后面加上了U。
from lecture.models import Lecture
class LectureAddForm(forms.Form):
name = forms.fields.CharField( #要填入Lecture模型中的字段
label = '讲座名称',
required =True,
min_length = 2,
max_length = 256,
error_messages={
"required":"讲座名称不可以为空!",
"min_length":"讲座名称不能低于2位!",
"max_length":"讲座名称不能超过256位!"
}
)
address = forms.fields.CharField(
label = '讲座地点',
required =True,
min_length = 4,
max_length = 256,
error_messages={
"required":"讲座地点不可以为空!",
"min_length":"讲座地点不能低于4位!",
"max_length":"讲座地点不能超过256位!"
}
)
start_time = forms.fields.CharField(
label = '开始时间',
required =True,
min_length = 18,
max_length = 19,
error_messages={
"required":"开始时间不可以为空!",
"min_length":"开始时间格式为yyyy-mm-dd hh:mm:ss不能低于18位!",
"max_length":"开始时间格式为yyyy-mm-dd hh:mm:ss不能高于19位!"
}
)
status = forms.fields.BooleanField(
label = '讲座状态',
required =False,
)
limits = forms.fields.IntegerField(
label = '限制人数',
required =True,
initial=1000,
error_messages={
"required":"限制人数不可以为空!",
}
)
lectureTeacher = forms.fields.ChoiceField(
label = '讲座老师',
required =True,
error_messages={
"required":"必须选择一个讲座老师!",
}
)
class LectureUpdateForm(forms.Form):
nameU = forms.fields.CharField( #要填入Lecture模型中的字段
label = '讲座名称',
required =True,
min_length = 2,
max_length = 256,
error_messages={
"required":"讲座名称不可以为空!",
"min_length":"讲座名称不能低于2位!",
"max_length":"讲座名称不能超过256位!"
}
)
addressU = forms.fields.CharField(
label = '讲座地点',
required =True,
min_length = 4,
max_length = 256,
error_messages={
"required":"讲座地点不可以为空!",
"min_length":"讲座地点不能低于4位!",
"max_length":"讲座地点不能超过256位!"
}
)
start_timeU = forms.fields.CharField(
label = '开始时间',
required =True,
min_length = 18,
max_length = 19,
error_messages={
"required":"开始时间不可以为空!",
"min_length":"开始时间格式为yyyy-mm-dd hh:mm:ss不能低于18位!",
"max_length":"开始时间格式为yyyy-mm-dd hh:mm:ss不能高于19位!"
}
)
statusU = forms.fields.BooleanField(
label = '讲座状态',
required =False,
)
limitsU = forms.fields.IntegerField(
label = '限制人数',
required =True,
initial=1000,
error_messages={
"required":"限制人数不可以为空!",
}
)
lectureTeacherU = forms.fields.ChoiceField(
label = '讲座老师',
required =True,
error_messages={
"required":"必须选择一个讲座老师!",
}
)
4 在lecture/views.py中定义视图函数query、add、update、delete
add()、update()函数存在添加,修改失败的问题;需要将信息传递到上述两个函数重定向到的query()函数中。详细说明在代码中有:
重定向传值是通过session来进行的。
详细代码如下:
from django.shortcuts import render,redirect
# Create your views here.
from lecture.models import Lecture
from teacher.models import Teacher
from lecture.forms import LectureAddForm,LectureUpdateForm
from django.core.paginator import Paginator
# 导入获取自定义User对象的方法
from django.contrib.auth import get_user_model
User = get_user_model() # 获取自定义User模型
##########################################
# Lecture 、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
'form': Lecture的新增表单
'formU': Lecture的修改表单
'search_name': 查询条件。供页面刷新时<input>显示最近一次输入值用
'app_name': 应用名称。
'id': 页面选择状态
'teacherList': <select>元素的<option>选项表
'code': add()、update()函数的状态码
'message': add()、update()函数的信息
'errorInfo': add()、update()函数的错误原因
'''
# 初始化返回值为空的字典
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 = Lecture.objects.filter(name__contains = request.POST['search_name']).order_by('start_time')
# 保存查询条件,供index.html页面要设置搜索栏表单中的相应输入框value属性用
context['search_name'] = request.POST['search_name']
else: # 查询所有
context['search_name'] = ''
varList = Lecture.objects.all().order_by('start_time')
# 创建分页器实例
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'] = Lecture.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
'''
将所有有关修改、新增所需要的基础数据列表传入前端
供前端<select>的<option>选项使用
'''
context['teacherList'] = querySetToList(Teacher.objects.all().order_by('id').values('id','name'))
context['form'] = LectureAddForm() #新增讲座用到的空表格
context['formU'] = LectureUpdateForm() #修改讲座用到的空表格
context['app_name'] = 'lecture'
return render(request, "index.html", context)
def querySetToList(querySet):
'''将querySet对象,转化为列表对象'''
varList =[]
for var in querySet:
varList.append( var)
return varList
def add(request):
# 1 处理POST请求
name=request.POST['name']
address=request.POST['address']
start_time = request.POST['start_time']
'''
模型中定义的status是Boolean类型
在定义表单相应的form时,该输入变成了type='checkbox'的<input>框。
status在前端反映的是输入类型checkbox的状态,勾选为true
不勾选则在request.POST中不存在这个键,所以不能直接用
request.POST['status']
而应该改用:
request.POST.get('status')
但传到后端来,变成了,勾选是on
不符合模型定义,尽管通过了表单清洗,还需要再清洗
'''
status = ''
if request.POST.get('status') == 'on':
status = True
else:
status = False
limits = request.POST['limits']
lectureTeacher_id = request.POST['lectureTeacher']
# 2 判断讲座名是否已经存在?
name_exists = Lecture.objects.filter(name= name).exists()
if name_exists: #如果讲座名称已经存在
# # 设置session, 操作状态
request.session['code'] = 400
request.session['message'] = '讲座添加失败'
request.session['errorINFO'] = "您输入的讲座名称已存在!"
print("您输入的讲座名称已存在!")
return redirect("/lecture/query")
# 3 保存到Lecture数据库
Lecture(
name=name,
address=address,
start_time = start_time,
status = status,
limits = limits,
lectureTeacher_id = lectureTeacher_id,
).save()
# 4 设置session
request.session['code'] = 200
request.session['message'] = '讲座添加成功'
request.session['errorINFO'] = ''
return redirect("/lecture/query/")
def update(request):
# 1 处理POST请求
id = request.POST['id']
current_page = request.POST["current_page"]
per_page = request.POST["per_page"]
name=request.POST['nameU']
address=request.POST['addressU']
start_time = request.POST['start_timeU']
status = ''
if request.POST.get('statusU') == 'on':
status = True
else:
status = False
limits = request.POST['limitsU']
lectureTeacher_id = request.POST['lectureTeacherU']
# 2 设置页面状态session
request.session['current_page'] = current_page
request.session['per_page'] = per_page
request.session['id'] = id
# 3 根据id 查询 修改前的User信息
lectureBeforUpdate = Lecture.objects.get(id = id)
# 4 判定讲座名称是否已经存在
name_exists = Lecture.objects.filter(name= name).exists()
if (name_exists) and (name !=lectureBeforUpdate.name ):#如果讲座名称已经存在,并且与修改前的不一致
# 设置session, 操作状态
request.session['code'] = 400
request.session['message'] = '讲座修改失败'
request.session['errorINFO'] = "您输入的讲座名称已存在!"
print("您输入的讲座名称已存在!")
return redirect("/lecture/query")
# 5 保存到Lecture数据库
Lecture.objects.filter(id= id).update(
name=name,
address=address,
start_time = start_time,
status = status,
limits = limits,
lectureTeacher_id = lectureTeacher_id,
)
# 6 设置session
# 操作状态
request.session['code'] = 200
request.session['message'] = '讲座修改成功'
request.session['errorINFO'] = ''
# 页面状态
request.session['current_page'] = current_page
request.session['per_page'] = per_page
request.session['id'] = id
return redirect("/lecture/query/")
def delete(request):
# 1 将前端传过来的idList字符串分割为数组
idList=request.POST['idList'].split(' ')
# 2 逐一删除表中数据
for var in idList: # 删除从表
Lecture.objects.filter(id=int(var)).delete()
return redirect("/lecture/query")
5 模板文件
除了lecture/module_query.html之外,其他的都可以用第9节的模板文件。
<!-- module_query.html 在应用lecture目录下的模板目录templates中的lecture子目录中
内 容:以<table>展示查询和模糊查询的结果。
在<table>第一列设置复选框,用于同时选择多条记录,共批量删除用。
在<table>最后一列为操作列。设置两个默认禁用图标按钮用于当前行的修改、删除。
涉及的变量:
{{result}}: 含有多条查询结果。
每一行的key和数据表中的字段名相同。若{{var}}是{{result}}的一行数据,
那么各列数据分别为:{{var.id}}、{{var.name}}、{{var.address}}、
{{var.start_time}}、{{var.status}}、{{var.limits}}、
{{var.lectureTeacher}}
-->
<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>
</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.address}}</h5></td>
<!-- Lecture中的start_time是日期时间字段,若不加任何处理
在页面显示的英文格式,所以要加过滤器date,格式为"Y-m-d H:i",
大写Y表示,年份是4位数,小写y表示年份是2位数,数据库存储需要年份为4位数
-->
<td class="start_time"> <h5>{{var.start_time|date:"Y-m-d H:i"}}</h5></td>
<td class="status"> <h5>{{var.status}}</h5></td>
<td class="limits"> <h5>{{var.limits}}</h5></td>
<td class="lectureTeacher"> <h5>{{var.lectureTeacher}}</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 在lecture/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,includeurlpatterns = [
path("admin/", admin.site.urls),
path('basedata/',include(('basedata.urls','basedata'))),
path('student/', include(('student.urls', 'student'))),path('teacher/', include(('teacher.urls', 'teacher'))),
path('lecture/', include(('lecture.urls', 'lecture'))),
]
8 访问http://127.0.0.1:8000/lecture/query/
可以得到与第9节类似的页面