框架第十三天周总结-----图书管理系统代码优化,加分页器,加了用户注册,登录,退出登录,登录成功自动显示登录用户的用户名等功能
settings 文件相关配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day14',
'HOST': '127.0.0.1',
'port': 3306,
'USER': 'root',
'PASSWORD': '222',
'CHARSET': 'utf8'
}
}
STATIC_URL = '/static1/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
AUTH_USER_MODEL = 'app01.UserInfo'
.
.
from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
# 扩展auth_user表
class Userinfo(AbstractUser):
phone = models.BigIntegerField(null=True)
desc = models.TextField(null=True)
class Book(models.Model):
"""图书表"""
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32, verbose_name='书名')
price = models.DecimalField(max_digits=9, decimal_places=3, verbose_name='价格')
publish_time = models.DateTimeField(auto_now_add=True)
"""一对多字段"""
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
"""多对多字段"""
authors = models.ManyToManyField(to='Author')
def __str__(self):
return f'图书对象<<*>>{self.title}'
class Publish(models.Model):
"""出版社表"""
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, verbose_name='出版社名称')
address = models.CharField(max_length=64, verbose_name='地址')
def __str__(self):
return f'出版社对象<<*>>{self.name}'
class Author(models.Model):
"""作者表"""
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, verbose_name='作者姓名')
age = models.IntegerField(verbose_name='年龄')
"""一对一字段"""
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
def __str__(self):
return f'作者对象<<*>>{self.name}'
class AuthorDetail(models.Model):
id = models.AutoField(primary_key=True)
phone = models.BigIntegerField(verbose_name='电话号码')
address = models.CharField(max_length=64, verbose_name='作者地址')
def __str__(self):
return f'作者详情表对象<<*>>{self.address}'
.
.
总路由
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# 路由分发
path('app01/', include('app01.urls'))
]
------------------------------------------------
分路由
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('home/', views.home_func, name='home_view'),
# 图书列表展示页
path('book_list/', views.book_list_func, name='book_list_view'),
# auth用户注册
path('register/', views.register_func, name='register_func_view'),
# auth用户登录
path('login/', views.login_func, name='login_func_view'),
# auth修改密码
path('set_pwd/', views.set_pwd_func, name='set_pwd_func_view'),
# auth用户退出登录
path('logout/', views.logout_func, name='logout_func_view'),
# 图书数据添加页
path('book_add/', views.book_add_func, name='book_add_view'),
# 图书数据编辑页
path('book_edit/<int:book_pk>', views.book_edit_func, name='book_edit_view'),
# 图书删除页
path('book_delete/<int:book_pk>', views.book_delete_func,name='book_delete_view')
]
.
.
视图层代码
from django.shortcuts import render, HttpResponse, redirect, reverse
# Create your views here.
from app01 import models
def home_func(request):
return render(request, 'homePage.html')
--------------------------------------------
from app01 import models
def register_func(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
confirm_password = request.POST.get('confirm_password')
if not password == confirm_password:
return HttpResponse('两次密码不一致')
# 校验用户名是否存在
res = models.Userinfo.objects.filter(username=username)
if res:
return HttpResponse('用户名已存在')
# 注册用户(create方法不能用)
models.Userinfo.objects.create_user(username=username, password=password)
return render(request, 'registerPage.html')
# 这个地方因为我用的是自己扩展的auth_user表也就是Userinfo表,别忘了去settings里面声明替换关系AUTH_USER_MODEL = 'app01.UserInfo'
# 否则就要导模块,导的这个User是一个类对应的就是auth_user表!!!
# 然后用auth_user表来进行数据查询与添加
from django.contrib.auth.models import User
res = models.User.objects.filter(username=username)
User.objects.create_user(username=username, password=password)
--------------------------------------------
from django.contrib import auth
def login_func(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 校验用户名与密码是否正确
user_obj = auth.authenticate(request, username=username, password=password)
# 如果用户名与密码正确,自动生成session数据
if user_obj:
auth.login(request, user_obj)
target_path = request.GET.get('next')
if target_path:
obj = redirect(target_path)
else:
obj = redirect(reverse('home_view'))
request.session.set_expiry(2419200)
return obj
else:
return HttpResponse('密码不正确')
return render(request, 'loginPage.html')
--------------------------------------------
# 写装饰器
from django.contrib.auth.decorators import login_required
@login_required(login_url='/app01/login/')
def book_list_func(request):
# 手动加分页器代码
per_page_num = 3 # 每页展示数据条数
current_page = request.GET.get('page', 1) # 按键正常拿值,拿不到就按1拿
# 防止用户乱输要跳转的页数,异常就展示第一页
try:
current_page = int(current_page)
except Exception:
current_page = 1
# 获取数据的总个数
all_book_queryset = models.Book.objects.all()
all_count = all_book_queryset.count()
# 获取页面的总个数
all_page_num, yushu = divmod(all_count, per_page_num)
if yushu:
all_page_num += 1
html_str = ''
xxx = current_page
yyy = current_page
if xxx < 6:
xxx = 6
if yyy > all_page_num - 5:
yyy = all_page_num - 5
# 注意这个地方玩的很骚,用变量名xxx指代current_page来控制分页的标签的个数与标签显示的样式,这地方分页标签的个数展示还有点问题(这个地方逻辑有点烦)
for i in range(xxx - 5, yyy + 6):
# 下面用还用current_page变量名来控制用户实际点的是哪一页,就将该分页显示选中
if current_page == i:
html_str += '<li class="active"> <a href="?page=%s">%s</a></li>' % (i, i)
else:
html_str += '<li> <a href="?page=%s">%s</a></li>' % (i, i)
# 每次for循环产生一个新的分页标签的代码的字符串,和原来的字符串进行一个拼接,
# 最后结束,所有的标签代码的字符串都拼接在一起,被html_str变量接收,传给html,利用过滤器 |safe 解析出代码
start_num = (current_page - 1) * per_page_num # 起始展示位置
end_num = current_page * per_page_num # 终止展示位置
part_book_queryset = all_book_queryset[start_num:end_num]
# 手动加分页器代码
# book_queryset = models.Book.objects.all() 原来这行代码就不能要了!!!
# 现在传给页面的是 part_book_queryset
return render(request, 'bookListPage.html', locals())
--------------------------------------------
@login_required(login_url='/app01/login/')
def set_pwd_func(request):
if request.method == 'POST':
old_pwd = request.POST.get('old_pwd')
new_pwd = request.POST.get('new_pwd')
confirm_pwd = request.POST.get('confirm_password')
print(new_pwd)
print(confirm_pwd)
if not new_pwd == confirm_pwd:
return HttpResponse('两次密码不一致')
# 判断原密码是否正确
is_right = request.user.check_password(old_pwd)
if not is_right:
return HttpResponse('原密码不正确')
# 修改密码
request.user.set_password(new_pwd)
request.user.save()
return HttpResponse('密码修改成功')
return render(request, 'setPwdPage.html')
--------------------------------------------
@login_required(login_url='/app01/login/')
def logout_func(request):
auth.logout(request)
return HttpResponse('退出登录')
--------------------------------------------
@login_required(login_url='/app01/login/')
def book_add_func(request):
# 2. 获取页面上用户输入的值
if request.method == 'POST':
title = request.POST.get('title')
price = request.POST.get('price')
publish_time = request.POST.get('publish_time')
publish_id = request.POST.get('publish_id')
author_id_list = request.POST.getlist('author_id_list')
# 3. 对获取的数据进行写判断
if len(title) == 0 or len(price) == 0:
return HttpResponse('书名与价格不能为空')
# 4. 判断书名是否已存在
res = models.Book.objects.filter(title=title)
if res:
return HttpResponse('书名已存在!')
# 5. 没有问题后往数据库里面书籍表里添加数据
book_obj = models.Book.objects.create(title=title,
price=price,
publish_time=publish_time,
publish_id=publish_id, )
book_obj.authors.add(*author_id_list)
# 6. 重定向到书籍展示页
return redirect(reverse('book_list_view'))
# 1. 分析可得html页面需要全部的出版社数据与全部的作者数据!!
publish_queryset = models.Publish.objects.all()
author_queryset = models.Author.objects.all()
return render(request, 'bookAddPage.html', locals())
---------------------------------------------------
@login_required(login_url='/app01/login/')
def book_edit_func(request, book_pk):
# 1. 根据传到视图函数的数据主键值查询数据对象
edit_book_obj = models.Book.objects.filter(pk=book_pk).first() # first()不要忘了
# 4. 获取用户编辑后发送的post请求后的数据
if request.method == 'POST':
title = request.POST.get('title')
price = request.POST.get('price')
publish_time = request.POST.get('publish_time')
publish_id = request.POST.get('publish_id')
author_id_list = request.POST.getlist('author_id_list')
# 对获取的数据进行写判断
if len(title) == 0 or len(price) == 0:
return HttpResponse('书名与价格不能为空')
# 5. 没有问题后往数据库里面书籍表里添加数据
models.Book.objects.filter(pk=book_pk).update(title=title,
price=price,
publish_time=publish_time,
publish_id=publish_id)
edit_book_obj.authors.set(author_id_list)
return redirect(reverse('book_list_view'))
# 3. 分析可得编辑页面也需要所有的出版社和作者信息!!!直接复制上面视图函数的代码!!
publish_queryset = models.Publish.objects.all()
author_queryset = models.Author.objects.all()
# 2. 传递给html页面,并展示给用户看
return render(request, 'bookEditPage.html', locals())
-------------------------------------------
@login_required(login_url='/app01/login/')
def book_delete_func(request, book_pk):
# 1. 先拿到书籍表要删除的书籍对象!!!
delete_book_obj = models.Book.objects.filter(pk=book_pk).first()
# 2. 再删除数据对象第3张表里面的与作者的对应关系
delete_book_obj.authors.clear()
# 3. 最后再删除该书籍对象!!!
models.Book.objects.filter(pk=book_pk).delete()
return redirect('book_list_view')
-------------------------------------------
.
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/static1/jQuery3.6.js"></script>
# 注意static1是路由,后面是该静态文件存与static文件夹下的路径!!!
{% load static %} # 接口前缀的动态匹配模板语法 此处static是个py文件,与路由无关
<link rel="stylesheet" href={% static "bootstrap-3.4.1-dist/css/bootstrap.min.css" %}>
<script src={% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}></script>
{% block css %}
{% endblock %}
</head>
<body>
<!--导航条-->
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">BMS</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">图书<span class="sr-only">(current)</span></a></li>
<li><a href="#">作者</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多 <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">快点行动</a></li>
<li><a href="#">等不了了</a></li>
<li><a href="#">快点我</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">更多美女</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">快速赚钱法</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="书名">
</div>
<button type="submit" class="btn btn-default">查询</button>
</form>
<ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li><a href="#">{{ request.user.username }}</a></li>
{% else %}
<li><a href="{% url 'register_func_view' %}">注册</a></li>
<li><a href="{% url 'login_func_view' %}">登录</a></li>
{% endif %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多操作<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'logout_func_view' %}">账号退出</a></li>
<li><a href="{% url 'set_pwd_func_view' %}">修改密码</a></li>
<li><a href="#">投诉建议</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">联系管理员</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<!--左边栏-->
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<div class="list-group" >
<a href="{% url 'home_view' %}" class="list-group-item active text-center" id="exit1">首页展示</a>
<a href="{% url 'book_list_view' %}" class="list-group-item text-center" id="exit2">图书列表</a>
<a href="#" class="list-group-item text-center" id="exit3">出版社列表</a>
<a href="#" class="list-group-item text-center" id="exit4">作者列表</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
<a href="#" class="list-group-item text-center">跟多详情</a>
</div>
</div>
<!--右边栏-->
<div class="col-md-10">
<!--面板-->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><span class="glyphicon glyphicon-th-list" id="span1"></span>图书管理系统</h3>
</div>
<div class="panel-body">
{% block content %}
<!--页头-->
<div class="page-header">
<h1>图书管理系统 <small id="small1">更多精彩等你来查</small></h1>
</div>
<!--巨幕-->
<div class="jumbotron" id="d1">
<h1>全球最大的网上图书管理系统</h1>
<p id="p1">友情连接</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">点击精彩</a>
<button type="button" class="btn btn-primary btn-lg" aria-label="Left Align">
<span class="glyphicon glyphicon-lock " aria-hidden="true"></span>
</button>
</p>
</div>
<!--"右下图片区"-->
<div class="row">
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<sapn class="glyphicon glyphicon-heart"></sapn> <sapn class="glyphicon glyphicon-heart"></sapn> <sapn class="glyphicon glyphicon-heart"></sapn>
<img src="xxx" alt="...">
</a>
</div>
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<sapn class="glyphicon glyphicon-heart"></sapn> <sapn class="glyphicon glyphicon-heart"></sapn> <sapn class="glyphicon glyphicon-heart"></sapn>
<img src="xxx" alt="...">
</a>
</div>
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<sapn class="glyphicon glyphicon-heart"></sapn> <sapn class="glyphicon glyphicon-heart"></sapn> <sapn class="glyphicon glyphicon-heart"></sapn>
<img src="xxx" alt="...">
</a>
</div>
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<sapn class="glyphicon glyphicon-heart"></sapn> <sapn class="glyphicon glyphicon-heart"></sapn> <sapn class="glyphicon glyphicon-heart"></sapn>
<img src="xxx" alt="...">
</a>
</div>
</div> <!--"内部的row"-->
{% endblock %}
</div> <!--"panel-body"-->
</div> <!--"panel panel-primary"-->
</div> <!--"col-md-10"-->
</div> <!--"row"-->
</div> <!--"container-fluid"-->
{% block js %}
{% endblock %}
</body>
</html>
.
.
{% extends 'homePage.html' %}
{% block css %}
{% endblock %}
<!--内容区-->
{% block content %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post">
{% csrf_token %}
<p>用户名:
<input type="text" name="username" class="form-control">
</p>
<p>密码:
<input type="text" name="password" class="form-control">
</p>
<p>确认密码:
<input type="text" name="confirm_password" class="form-control">
</p>
<input type="submit" value="注册" class="btn btn-primary btn-block">
</form>
</div>
</div>
{% endblock %}
{% block js %}
{% endblock %}
.
.
{% extends 'homePage.html' %}
{% block css %}
{% endblock %}
<!--内容区-->
{% block content %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post">
{% csrf_token %}
<p>用户名:
<input type="text" name="username" class="form-control">
</p>
<p>密码:
<input type="text" name="password" class="form-control">
</p>
<input type="submit" value="登录" class="btn btn-primary btn-block">
</form>
</div>
</div>
{% endblock %}
。
。
改密码
{% extends 'homePage.html'%}
{% block css %}
{% endblock %}
<!--内容区-->
{% block content %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post">
{% csrf_token %}
<p>原密码:
<input type="text" name="old_pwd" class="form-control">
</p>
<p>新密码:
<input type="text" name="new_pwd" class="form-control">
</p>
<p>确认密码:
<input type="text" name="confirm_password" class="form-control">
</p>
<input type="submit" value="确认修改密码" class="btn btn-primary btn-block">
</form>
</div>
</div>
{% endblock %}
{% block js %}
{% endblock %}
.
.
booklistpage
{% extends 'homePage.html' %}
{% block css %}
{% endblock %}
<!--内容区-->
{% block content %}
<h2 class="text-center">图书展示页</h2>
<a href="{% url 'book_add_view' %}" class="btn btn-warning">图书添加</a>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>编号</th>
<th>书名</th>
<th>价格</th>
<th>出版日期</th>
<th>出版社</th>
<th>作者</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for book_obj in part_book_queryset %} <!--QuerySet[书对象,书对象]-->
<tr>
<td>{{ forloop.counter }}</td> <!--可以展示主键值也可以展示从一开始的有序数列-->
<td>{{ book_obj.title }}</td>
<td>{{ book_obj.price }}</td>
<td>{{ book_obj.publish_time|date:'Y-m-d' }}</td>
<td class="btn btn-info">{{ book_obj.publish.name }}</td>
<td ><!--注意多对多的这张表是一个虚拟表,通过书对象点虚拟外键字段authors只会跳到Author作者表,不会往虚拟表上添加!!-->
{% for author_obj in book_obj.authors.all %} <!--QuerySet[作者对象,作者对象]-->
<span style="margin-right: 5px" class="btn btn-success">{{ author_obj.name }}</span>
{# {% if forloop.last %}#}
{# {{ author_obj.name }}#}
{# {% else %}#}
{# {{ author_obj.name }} /#}
{# {% endif %} <!--for循环出的是最后一个不加分隔符,其他的末尾加个分隔符!!-->#}
{% endfor %}
</td>
<td>
<!--先拿到别名'book_edit_view'反向解析出路由book_edit/ 再加上for循环出的每一个书籍对象的主键值,一起拼出新的路由-->
<!--a标签的作用是跳转到这个新的路由网址上去,跳转到新的路由网址上去后,首先还是要先到路由层匹配视图函数-->
<!--当匹配上后,将request与book_pk=从网址上匹配到的动态的路由结果,再传递给视图函数!!-->
<a href="{% url 'book_edit_view' book_obj.pk %} " class="btn btn-primary btn-xs">编辑</a>
<a href=" " class="btn btn-danger btn-xs delBtn">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!--分页器!!-->
<nav aria-label="Page navigation" class="text-center">
<ul class="pagination"> <!--这个是属性是让列表标签浮动起来,让原来竖排变成横排!!-->
<!--往左的小按钮-->
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li><a href="#">1</a></li> <!--原来手动写的分页标签全部不要-->
<li><a href="#">2</a></li> <!--原来手动写的分页标签全部不要-->
<li><a href="#">3</a></li> <!--原来手动写的分页标签全部不要-->
{{ html_str|safe }} <!--全部让后端自动生成好了传过来-->
<!--往右的小按钮-->
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
{% endblock %}
<!--js区-->
<!--给删除按钮添加一个删除点击确认事件!!-->
{% block js %}
<script>
$('.delBtn').click(function () {
let res = confirm('你确定要删除吗?')
if (!res){
return false
}
})
</script>
{% endblock %}
。
。
bookaddpage
{% extends 'homePage.html' %}
{% block css %}
{% endblock %}
<!--内容区-->
{% block content %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">图书添加页</h2>
<form action="" method="post">
{% csrf_token %}
<p> 书名:
<input type="text" name="title" class="form-control">
</p>
<p> 价格:
<input type="text" name="price" class="form-control">
</p>
<p> 出版日期:
<input type="date" name="publish_time" class="form-control">
</p>
<p>出版社名称:
<!--展示系统中已有的出版社信息供选择-->
<select name="publish_id" id="" class="form-control">
{% for publish_obj in publish_queryset %}
<option value="{{ publish_obj.id }}">{{ publish_obj.name }}</option>
{% endfor %}
</select>
</p>
<p>作者名称list:
<select name="author_id_list" id="" multiple class="form-control">
{% for author_obj in author_queryset %}
<option value="{{ author_obj.id }}">{{ author_obj.name }}</option>
{% endfor %}
</select>
</p>
<input type="submit" value="书籍的添加" class="btn btn-block btn-success">
</form>
</div>
</div>
{% endblock %}
{% block js %}
{% endblock %}
.
.
bookeditpage
<!--书籍编辑页-->
{% extends 'homePage.html' %} <!--继承母板-->
<!--css区-->
{% block css %}
{% endblock %}
<!--内容区-->
{% block content %}
<div class="row">
<div class="col-md-6">
<h2 class="text-center">图书编辑页</h2>
<form action="" method="post" >
{% csrf_token %}
<p> 书名:
<input type="text" name="title" class="form-control" value="{{ edit_book_obj.title }}">
</p>
<p> 价格:
<input type="text" name="price" class="form-control" value="{{ edit_book_obj.price }}">
</p>
<p> 出版日期:
<input type="date" name="publish_time" class="form-control" value="{{ edit_book_obj.publish_time|date:'Y-m-d' }}">
</p>
<p> 出版社名称:
<!--展示系统中已有的出版社信息供选中-->
<select name="publish_id" id="" class="form-control">
{% for publish_obj in publish_queryset %} <!--queryset [数据对象,数据对象]-->
<!--根据待编辑的书籍对象查询对应的出版社对象,与系统所有的出版社对象比对,如果相同则添加默认选中的属性selected-->
{% if edit_book_obj.publish == publish_obj %}
<option value="{{ publish_obj.id }}"selected>{{ publish_obj.name }}</option>
{% else %}
<option value="{{ publish_obj.id }}">{{ publish_obj.name }}</option>
{% endif %}
{% endfor %}
</select>
</p>
<p> 作者名称list:
<select name="author_id_list" id="" multiple class="form-control"> <!--一个图书可以有多个作者,加个multiple可以多选-->
{% for author_obj in author_queryset %} <!--queryset [数据对象,数据对象]-->
<!--成员运算判断判断,循环的作者对象是否在待编辑的书籍作者对象列表中-->
{% if author_obj in edit_book_obj.authors.all %}
<option value="{{ author_obj.id }}"selected>{{ author_obj.name }}</option>
{% else %}
<option value="{{ author_obj.id }}">{{ author_obj.name }}</option>
{% endif %}
{% endfor %}
</select>
</p>
<input type="submit" value="书籍的编辑" class="btn btn-block btn-danger">
</form>
</div> <!--"col-md-6"-->
<div class="col-md-6">
<img style="max-width:100%" src="https://file.moyublog.com/d/file/2021-02-14/c7e482715ac8cd045c3964b147231ee8.jpg" alt="..." class="img-rounded">
</div>
</div> <!--"row"-->
{% endblock %}
<!--js区-->
{% block js %}
{% endblock %}
.
.
.
补充:使用自定义分页器的情况:
@login_required(login_url='/app01/login/')
def book_list_func(request):
all_book_queryset = models.Book.objects.all()
# 用自定义分页器
from app01.others.mypage import Pagination # 导写好的分页器类的py文件!
current_page = request.GET.get('page', 1) # 按键正常拿值,拿不到就按1拿
all_count = all_book_queryset.count()
# 只需要传两个参数就行,每页显示的数据条数与与最多显示的页码格式,类里面已经提前写好了,要改去类里面改
page_obj = Pagination(current_page=current_page, all_count=all_count)
page_queryset = all_book_queryset[page_obj.start:page_obj.end]
return render(request, 'bookListPage111.html', locals())
-----------------------------------
html代码:
{% extends 'homePage.html' %}
{% block css %}
{% endblock %}
<!--内容区-->
{% block content %}
<h2 class="text-center">图书展示页</h2>
<a href="{% url 'book_add_view' %}" class="btn btn-warning">图书添加</a>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>编号</th>
<th>书名</th>
<th>价格</th>
<th>出版日期</th>
<th>出版社</th>
<th>作者</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for book_obj in page_queryset %} <!--QuerySet[书对象,书对象]-->
<tr>
<td>{{ forloop.counter }}</td> <!--可以展示主键值也可以展示从一开始的有序数列-->
<td>{{ book_obj.title }}</td>
<td>{{ book_obj.price }}</td>
<td>{{ book_obj.publish_time|date:'Y-m-d' }}</td>
<td class="btn btn-info">{{ book_obj.publish.name }}</td>
<td ><!--注意多对多的这张表是一个虚拟表,通过书对象点虚拟外键字段authors只会跳到Author作者表,不会往虚拟表上添加!!-->
{% for author_obj in book_obj.authors.all %} <!--QuerySet[作者对象,作者对象]-->
<span style="margin-right: 5px" class="btn btn-success">{{ author_obj.name }}</span>
{# {% if forloop.last %}#}
{# {{ author_obj.name }}#}
{# {% else %}#}
{# {{ author_obj.name }} /#}
{# {% endif %} <!--for循环出的是最后一个不加分隔符,其他的末尾加个分隔符!!-->#}
{% endfor %}
</td>
<td>
<!--先拿到别名'book_edit_view'反向解析出路由book_edit/ 再加上for循环出的每一个书籍对象的主键值,一起拼出新的路由-->
<!--a标签的作用是跳转到这个新的路由网址上去,跳转到新的路由网址上去后,首先还是要先到路由层匹配视图函数-->
<!--当匹配上后,将request与book_pk=从网址上匹配到的动态的路由结果,再传递给视图函数!!-->
<a href="{% url 'book_edit_view' book_obj.pk %} " class="btn btn-primary btn-xs">编辑</a>
<a href="{% url 'book_delete_view' book_obj.pk %} " class="btn btn-danger btn-xs delBtn">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!--分页器!!-->
<!--已经把所有分页的代码,全部封装好了,就一行代码完事-->
{{ page_obj.page_html|safe }}
{% endblock %}
<!--js区-->
<!--给删除按钮添加一个删除点击确认事件!!-->
{% block js %}
<script>
$('.delBtn').click(function () {
let res = confirm('你确定要删除吗?')
if (!res){
return false
}
})
</script>
{% endblock %}
.
.
别人写好的分页器类代码(mypage.py)
# 把整个分页器代码全部封装到后端来写
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=3, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param pager_count: 最多显示的页码个数(或者说分页器标签的个数)
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page_num = per_page_num
# 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示11/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1
page_html_list = []
# 添加前面的nav和ul标签
page_html_list.append('''
<nav aria-label='Page navigation>'
<ul class='pagination'>
''')
first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
</nav>
</ul>
''')
return ''.join(page_html_list)
.
.
分页器标签在页面渲染的页码个数(或者说分页器标签的个数) 控制思路
当不用别人写好的类,自己写分页器代码的时候,发现鬼玩意真不好写
上面我自己写的分页器标签有点问题:
---------------------------------
html_str = ''
xxx = current_page
yyy = current_page
if xxx < 6:
xxx = 6
if yyy > all_page_num - 5:
yyy = all_page_num - 5
# 注意这个地方玩的很骚,用变量名xxx指代current_page来控制分页的标签的个数与标签显示的样式,这地方分页标签的个数展示还有点问题(这个地方逻辑有点烦)
for i in range(xxx - 5, yyy + 6):
# 下面用还用current_page变量名来控制用户实际点的是哪一页,就将该分页显示选中
if current_page == i:
html_str += '<li class="active"> <a href="?page=%s">%s</a></li>' % (i, i)
else:
html_str += '<li> <a href="?page=%s">%s</a></li>' % (i, i)
# 每次for循环产生一个新的分页标签的代码的字符串,和原来的字符串进行一个拼接,
# 最后结束,所有的标签代码的字符串都拼接在一起,被html_str变量接收,传给html,利用过滤器 |safe 解析出代码
---------------------------------------
问题在哪儿了,虽然当前的方法可以控制分页器标签不在显示负数 ,以及不再显示的页码数大于总页码,但是还是有问题,比如我选择页面数为2的时候,显示的确实1-7分页标签,为什么会这样?
因为当前页为2的时候,yyy并没有大于all_page_num - 5,所以不会将yyy变成all_page_num - 5
yyy还是2,所以 range(1,8) 所以最后渲染出7个标签出来
分析思路如下:
current_page: 当前页
all_count: 数据库中的数据总条数
per_page_num: 每页显示的数据条数
pager_count: 最多显示的页码个数(或者说分页器标签的个数) 假设我们定为11
---------------------------
range(pager_start,pager_end)
总共4种情况:对应的range括号里面的初始与结束的页码数都不一样!!!
-----------------
第一种情况:总页码小于我们定的最多显示的分页器标签个数时,很显然要按实际我们的总页码来,将所有页码都显示出来即可所以:
pager_start = 1
pager_end = all_pager(总页码) + 1
------------------------------------------
第二种情况:总页码大于我们定的最多显示的分页器标签个数(11)时,且当前我们选择跳转的页码小于最多显示的页码的个数的一半时,比如1,2,3,4,5时:
此时开始页肯定还是从1开始,但结束页就应该是11(最多显示的分页器标签的个数),也就是说当选择跳转的页面小于最多显示的页码的个数的一半时,按理就应该让页面显示的标签就是1-11共11个分页标签
pager_start = 1
pager_end =pager_count + 1
-------------------------------------------
第三种情况:总页码大于我们定的最多显示的分页器标签个数(11)时,当前选择跳转的页大于5,且总页码数大于(当前页+5)时,比如总页码是100页,当前页是8页,这种情况下,按理就应该让页面显示的标签就是3-13共11个分页标签,当前页在中间
pager_start = current_page(当前页) - pager_count_half(11/2最多显示页码个数除2就是5)
pager_start = 当前页 - 5
pager_end = 当前页 + pager_count_half(就5)+ 1 = 当前页 + 6
---------------------------------------------
第四种情况:总页码大于我们定的最多显示的分页器标签个数(11)时,当前选择跳转的页大于5,且总页码数小于(当前页+5)时,比如:当前页是9页,总页码是13页,这种情况下,按理就应该让页面显示的标签就是3-13共11个分页标签才对
pager_start = all_pager(总页码)- 最多允许显示的分页器标签个数(11) + 1
pager_end = all_pager(总页码) + 1
---------------------------------------------
那也就是说上面的 代码要这样改:
html_str = ''
#
pager_count_half = int((pager_count - 1) / 2)
if all_page_num <= pager_count: # 如果总页码 < 11个:
pager_start = 1
pager_end = all_page_num + 1
# 总页码 > 11 页
else:
# 且当前页如果<=页面上最多显示个页码(11-1)/2时,就是当前页小于5页
if current_page <= pager_count_half:
pager_start = 1
pager_end = pager_count + 1
# 且当前页大于5页情况:
else:
# 当前页加5页大于总页数了,分页标签直接从最后页往前显示11个分页标签就行了(页码翻到最后)
if (current_page + pager_count_half) > all_page_num:
pager_end = all_page_num + 1
pager_start = all_page_num - pager_count + 1
# 当前页加5页小于总页数了,这就是最正常的情况(总页码>11页,且当前页大于5,且当前页加5小于总页数),用当前页来控制起始
else:
pager_start = current_page - pager_count_half # 当前页-5
pager_end = current_page + pager_count_half + 1 # 当前页+6
for i in range(pager_start, pager_end):
# 下面用还用current_page变量名来控制用户实际点的是哪一页,就将该分页显示选中
if current_page == i:
html_str += '<li class="active"> <a href="?page=%s">%s</a></li>' % (i, i)
else:
html_str += '<li> <a href="?page=%s">%s</a></li>' % (i, i)
---------------------------------------------
这样改完分页器 还有一个地方没改好了,就是每页器左右两边的前进与后退的按钮,目前没有作用,具体怎么改,参考别人写好的分页器类怎么写的,再改!!!视图函数再加代码
if current_page <= 1:
html_prev_str = '<li class="disabled"><a href="#">上一页</a></li>'
else:
html_prev_str = '<li><a href="?page=%s">上一页</a></li>' % (current_page - 1,)
if current_page >= all_page_num:
html_next_str = '<li class="disabled"><a href="#">下一页</a></li>'
else:
html_next_str = '<li><a href="?page=%s">下一页</a></li>' % (current_page + 1,)
class="disabled 让该标签显示,当不再具有功能,禁用了。非常符合当点到第一页的时候,给向左的按钮禁掉不让用
-------
html页面原来的往左与往右的小按钮代码全部不需要了,直接就一行代码
<!--往左的小按钮-->
{# <li>#}
{# <a href="#" aria-label="Previous">#}
{# <span aria-hidden="true">«</span>#}
{# </a>#}
{# </li>#}
{{ html_prev_str|safe }} # 往左按钮
{{ html_str|safe }} # 分页标签的按钮
{{ html_next_str|safe }} # 往右按钮
--------------------------------------------
最后如果还想加个首页与尾页的按钮,还是一样抄别人类里面怎么写的。
视图函数再加代码:
first_page_str = '<li><a href="?page=%s">首页</a></li>' % (1)
last_page_str = '<li><a href="?page=%s">尾页</a></li>' % (all_page_num,)
html页面再加代码:
{{ first_page_str|safe }}
{{ html_prev_str|safe }}
{{ html_str|safe }}
{{ html_next_str|safe }}
{{ last_page_str|safe }}
---------------------------------------------
一个鸟分页器在分页器标签的显示个数这绕的一笔!!! 不如直接用别人写好的分页器类!!!!
创个others文件夹,穿个py文件,把别人写好的分页器类代码复制粘贴进去,保存一下
哪个视图函数需要用分页器,再该视图函数内部导入该分页器类,先拿到当前页与总页数,然后分页器类名加括号生成对象的同时,传给分页器对象。然后分页器对象点start与end拿到值对总的equeryset对象做切片操作!!! 对象点的start方法其实就是我们自己手写的方法
start_num = (current_page - 1) * per_page_num # 起始展示对象的位置索引
---------
def book_list_func(request):
all_book_queryset = models.Book.objects.all()
# 用自定义分页器
from app01.others.mypage import Pagination # 导写好的分页器类的py文件!
current_page = request.GET.get('page', 1) # 按键正常拿值,拿不到就按1拿
all_count = all_book_queryset.count()
# 只需要传两个参数就行,每页显示的数据条数与与最多显示的页码格式,类里面已经提前写好了,要改去类里面改
page_obj = Pagination(current_page=current_page, all_count=all_count)
page_queryset = all_book_queryset[page_obj.start:page_obj.end]
return render(request, 'bookListPage111.html', locals())
最终用自己的分页器也能达到效果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY