图书管理系统及django聚合查询、分组查询、F与Q查询
图书管理系统
图书管理系统基础环境准备
1.pycharm新建一个django项目
2.新建一个数据库用于存储项目所需数据
create database day07
3.配置pycharm基础环境配置
1)创建static目录
2)static目录拷贝静态资源目录
3)settings.py添加templates路径
TEMPLATES = [
DIRS': [os.path.join(BASE_DIR,'templates')]
4)settings.py添加static目录路径
STATICFILES_DIRS = [os.path.join(BASE_DIR,'static'),]
6)settings.py添加数据库支持
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day07',
'USER': 'root',
'PASSWORD': 'Abcd1234',
'HOST': '127.0.0.1',
'PORT': 3306,
'CHARSET': 'utf8',
}
}
7)settings.py关闭csfr配置
MIDDLEWARE = [
# 'django.middleware.csrf.CsrfViewMiddleware',
ps:如果不是通过pycharm创建应用,需要到settings.py注册手动新建的app
图书管理系统表设计及创建
1.表设计
创建4个表:Book、Publish、Author、Author_Detail。
表名:书籍表、出版社表、作者表和作者详情表。
先考虑表普通字段,在考虑外键设计
表普通字段:
Book:title、price、publish_time
Publish:name、address
Author:name、age
Author_Detail:phone、address
外键字段:
书籍表和出版社表关系推演:
书籍表:一本书是否可以有多个出版社:不可以
出版社:一个出版社是否可以出版多本书:可以
结论:一对多,外键建立在多的一方,即书籍表
书籍表和作者表关系推演:
书籍表:一本书是否可以有多个作者:可以
作者表:一个作者是否可以出版多本书:可以
结论:多对多,通过第三张表创建,ORM支持在任意一张创建外键字段自动创建第三张关系表
作者表和作者详情表关系推演:
作者表:每个作者是否可以有多个具体作者详情:不可以
作者详情表:每个详情是否可以包含多个作者:不可以
结论:一对一,建议外键字段建立在访问频率更高的表上,因此建议在作者表建立外键
models.py
class Book(models.Model):
"""图书表"""
title = models.CharField(max_length=32,verbose_name='书名')
price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name='价格')
publish_time = models.DateField(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):
"""出版社表"""
name=models.CharField(max_length=32,verbose_name='名称')
address = models.CharField(max_length=64,verbose_name='地址')
class Author(models.Model):
"""作者表"""
name = models.CharField(max_length=32,verbose_name='姓名')
age = models.IntegerField(verbose_name='年龄')
# 作者与作者详情的一对一关系
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
class AuthorDetail(models.Model):
"""作者详情表"""
phone = models.BigIntegerField(verbose_name='电话号码')
address=models.CharField(max_length=64,verbose_name='家庭地址')
# 数据库迁移
打开pycharm终端命令行(Terminal):
D:\pythonProject\djangoday07>python38 manage.py makemigrations
D:\pythonProject\djangoday07>python38 manage.py migrate
# 手动录入数据
ps:先录入没有外键字段表,在录入外键字段表。
通过pycharm自带的数据库连接手动录入数据:
Book:
Publish:
Author:
AuthorDetail:
authors:
图书管理系统之首页展示
urls.py:
from BookManageSystem import views
# 首页展示(也可以考虑使用路由分发)
path('home/',views.home_func,name='home_view'),
views.py:
from django.shortcuts import render,redirect,HttpResponse,reverse
from BookManageSystem import models
def home_func(request):
return render(request, 'homePage.html')
homePage.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.css' %}">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.js' %}"></script>
</head>
<body>
<nav class="navbar navbar-default">
<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="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</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">
<li><a href="#">jason</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="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</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">
首页展示
</a>
<a href="#" class="list-group-item">图书列表</a>
<a href="#" class="list-group-item">出版社列表</a>
<a href="#" class="list-group-item">作者列表</a>
<a href="#" class="list-group-item">更多详情</a>
</div>
<div class="list-group">
<a href="#" class="list-group-item active">
首页展示
</a>
<a href="#" class="list-group-item">图书列表</a>
<a href="#" class="list-group-item">出版社列表</a>
<a href="#" class="list-group-item">作者列表</a>
<a href="#" class="list-group-item">更多详情</a>
</div>
<div class="list-group">
<a href="#" class="list-group-item active">
首页展示
</a>
<a href="#" class="list-group-item">图书列表</a>
<a href="#" class="list-group-item">出版社列表</a>
<a href="#" class="list-group-item">作者列表</a>
<a href="#" class="list-group-item">更多详情</a>
</div>
</div>
<div class="col-md-10">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">BMS</h3>
</div>
<div class="panel-body">
<div class="page-header">
<h1>图书管理系统 <small>更多经常等你来玩</small></h1>
</div>
<div class="jumbotron">
<h1>全球最大的图书网址!</h1>
<p>友情链接</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">点击精彩</a></p>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="https://img0.baidu.com/it/u=2693313206,3402454070&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750" alt="...">
</a>
</div>
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="https://img0.baidu.com/it/u=2693313206,3402454070&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750" alt="...">
</a>
</div>
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="https://img0.baidu.com/it/u=2693313206,3402454070&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750" alt="...">
</a>
</div>
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="https://img0.baidu.com/it/u=2693313206,3402454070&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750" alt="...">
</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
首页布局:
1.先弄个导航条的可访问性,就是导航条带连接跳转的,编辑里面相关数据。
2.弄个容器布局,要全部占满页面的,container-fluid(div)
3.在容器里面弄个row(div),方便后面分12格.
4.弄两个col-md-2,col-md-10.
5.col-md-2(div)里面弄个列表组,弄成3份
6.col-md-10(div),添加面板,在面板里增加页头和巨幕,最后放4张缩略图。
图书管理系统之书籍展示
urls.py:
# 图书列表展示页
path('book_list/',views.book_list_func, name='book_list_view'),
views.py:
def book_list_func(request):
# 1.获取所有的图书数据
book_queryset = models.Book.objects.all() # queryset[数据对象,数据对象...]
# 2.返回html页面并在页面上展示图书数据
return render(request,'bookListPage.html',locals())
# locals() 指获取book_queryset对象并把该变量传递给bookListPage.html页面,方便html展示数据
homePage.html:
2.在<div class="col-md-10">下面添加模板,用于后面其它页面继承添加想要修改的内容
{% block content %}
把原来容器10格里面的内容全部粘贴进来
{% endblock %}
1.首页展示栏左侧修改图书列表链接跳转:
<a href="{% url 'book_list_view' %}" class="list-group-item">图书列表</a>
3.bookListPage.html:
{% extends 'homePage.html' %}
{% block content %}
<h2 class="text-center">图书展示页</h2>
<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 book_queryset %} <!--book_queryset=[数据对象,数据对象,数据对象]-->
<tr>
<td>{{ forloop.counter }}</td> <!--可以展示主键值 也可以展示从1开始的有序数列-->
<td>{{ book_obj.title }}</td>
<td>{{ book_obj.price }}</td>
<td>{{ book_obj.publish_time|date:'Y-m-d' }}</td>
<td>{{ book_obj.publish.name }}</td>
<td>{% for author_obj in book_obj.authors.all %} <!--queryset [作者对象,作者对象]-->
{% if forloop.last %}
{{ author_obj.name }}
{% else %}
{{ author_obj.name }},
{% endif %}
{% endfor %}
</td>
<td>
<a href="#" class="btn btn-primary btn-xs">编辑</a>
<a href="#" class="btn btn-danger btn-xs">删除</a>
</td>
<td></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>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
{% endblock %}
图书管理系统之书籍添加
urls.py:
# 图书数据添加页
path('book_add/',views.book_add_func,name='book_add_view'),
views.py:
def book_add_func(request):
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') # 获取完整的列表数据
# TODO:一些小判断自己完成
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) # add(1,2,3)
#书籍添加和用户有关系,所以需要操作第三张表添加书籍对应的作者,这句话表示通过当前书籍对象到第三张表authors,给这个书籍对象添加作者ID为author_id_list里面的所有ID
# 重定向到书籍展示页
return redirect('book_list_view') # 只支持没有动态匹配的别名反向解析
#2. 分析可得页面需要出版社和作者的全部数据
publish_queryset = models.Publish.objects.all()
author_queryset = models.Author.objects.all()
# 1.返回一个可以让用户输入书籍相关数据的html页面
return render(request,'bookAddPage.html',locals())
homeListPage.html:
添加图书添加按钮
<a href="{% url 'book_add_view' %}" class="btn btn-success">图书添加</a>
bookAddPage.html:
{% extends 'homePage.html' %}
{% block content %}
<h2 class="text-center">图书添加页</h2>
<form action="" method="post">
<p>title:
<input type="text" name="title" class="form-control">
</p>
<p>price:
<input type="text" name="price" class="form-control">
</p>
<p>publish_time:
<input type="date" name="publish_time" class="form-control">
</p>
<p>publish_name:
<!--展示系统中已有的出版社信息供选择 -->
<select name="publish_id" id="" class="form-control">
{% for publish_obj in publish_queryset %} <!--queryset [数据对象,数据对象] --> pk表示表的主键
<option value="{{ publish_obj.pk }}">{{ publish_obj.name }}</option>
{% endfor %}
</select>
</p>
<p>author_list:
<!--展示系统中已有的作者信息供选择 -->
<select name="author_id_list" id="" multiple class="form-control">
{% for author_obj in author_queryset %} <!--queryset [数据对象 数据对象] -->
<option value="{{ author_obj.pk }}">{{ author_obj.name }}</option>
{% endfor %}
</select>
</p>
<input type="submit" value="书籍添加" class="btn btn-block btn-success">
</form>
{% endblock %}
图书管理系统书籍编辑
urls.py:
# 图书数据编辑页
path('book_edit/<int:book_pk>/',views.book_edit_func,name='book_edit_view'), # book_edit_func(request对象,book_pk=’‘)
#为啥加<int:book_pk>动态解析,因为后端要获取用户编辑具体某项数据的ID值,所以加上方便后台和前端页面做数据处理
views.py
def book_edit_func(request,book_pk):
# 1.根据匹配到的数据主键值查询数据对象
edit_obj = models.Book.objects.filter(pk=book_pk).first()
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') # 获取完整的列表数据
models.Book.objects.filter(pk=book_pk).update(title=title,price=price,publish_time=publish_time,
publish_id=publish_id)
edit_obj.authors.set(author_id_list)
#同步多对多第三张表数据,给书籍对象更新对应作者,set(必须放元组或者列表)
return redirect('book_list_view')
#3.根据分析可得也需要所有出版社和作者的信息
publish_queryset = models.Publish.objects.all()
author_queryset = models.Author.objects.all()
# 2. 传递给html页面并展示给用户看
return render(request,'bookEditPage.html',locals())
bookListPage.html:
<a href="{% url 'book_edit_view' book_obj.pk %}" class="btn btn-primary btn-xs">编辑</a>
bookEditPage.html:
{% extends 'homePage.html' %}
{% block content %}
<h2 class="text-center">图书编辑页</h2>
<form action="" method="post">
<p>title:
<input type="text" name="title" class="form-control" value="{{ edit_obj.title }}">
</p>
<p>price:
<input type="text" name="price" class="form-control" value="{{ edit_obj.price }}">
</p>
<p>publish_time:
<input type="date" name="publish_time" class="form-control" value="{{ edit_obj.publish_time|date:'Y-m-d' }}">
</p>
<p>publish_name:
<!--展示系统中已有的出版社信息供选择 -->
<select name="publish_id" id="" class="form-control">
{% for publish_obj in publish_queryset %} <!--queryset [数据对象,数据对象] -->
<!--根据待编辑的书籍对象查询对应的出版社 与系统所有的出版社比对 如果相同则添加默认选中的属性 selected -->
{% if edit_obj.publish == publish_obj %}
<option value="{{ publish_obj.pk }}" selected>{{ publish_obj.name }}</option>
{% else %}
<option value="{{ publish_obj.pk }}">{{ publish_obj.name }}</option>
{% endif %}
{% endfor %}
</select>
</p>
<p>author_list:
<!--展示系统中已有的作者信息供选择 -->
<select name="author_id_list" id="" multiple class="form-control">
{% for author_obj in author_queryset %} <!--queryset [数据对象 数据对象] -->
<!--成员运算判断循环的作者对应是否在待编辑的书籍作者对象列表中 -->
{% if author_obj in edit_obj.authors.all %}
<option value="{{ author_obj.pk }}" selected>{{ author_obj.name }}</option>
{% else %}
<option value="{{ author_obj.pk }}">{{ author_obj.name }}</option>
{% endif %}
{% endfor %}
</select>
</p>
<input type="submit" value="书籍编辑" class="btn btn-block btn-success">
</form>
{% endblock %}
图书管理系统书籍删除
urls.py:
# 图书数据删除接口
path('book_delete/<int:book_pk>/', views.book_delete_func, name='book_delete_view'),
views.py:
def book_delete_func(request, book_pk):
# 删除书籍与作者的绑定关系
delete_book_obj = models.Book.objects.filter(pk=book_pk).first()
delete_book_obj.authors.clear()
# 删除书籍数据
models.Book.objects.filter(pk=book_pk).delete()
return redirect('book_list_view')
bookListPage.html:
<a href="{% url 'book_delete_view' book_obj.pk %}" class="btn btn-danger btn-xs delBtn">删除</a>
{% block jss %}
<script>
$('.delBtn').click(function (){
let isDel = confirm('你确定要删除吗')
if (!isDel){
return false
}
})
</script>
{% endblock %}
聚合查询
聚合函数:Max Min Sum Count Avg
在ORM中支持单独使用聚合函数 aggregate
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoday07.settings')
import django
django.setup()
from BookManageSystem import models
# 聚合函数需要导入模块才可以使用
from django.db.models import Max,Min,Sum,Count,Avg
res = models.Book.objects.aggregate(Max('price'),Count('pk'),最小价格=Min('price'),allPrice=Sum('price'),平均价格=Avg('price'))
print(res)
main()
分组查询
"""
如果执行orm分组查询报错 并且由关键字sql_mode strict mode
解决:移除sql_mode中的only_full_group_by
"""
# 分组查询(重要)
# 统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title','author_num')
print(res)
# 统计每个出版社卖的最便宜的书的价格
res=models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)
# 统计不止一个作者的图书
# 1.先统计每本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk'))
# 2.筛选出作者个数大于1的数据
res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title','author_num')
print(res)
# 查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price'),count_book=Count('book__pk')).values('name','sum_price','count_book')
print(res)
"""
models.表名.objects.annotate() 按照表分组
models.表名.objects.values('字段名').annotate() 按照values括号内指定的字段分组
"""
# 查询书籍里面各出版社出版了总计多少书
res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id','count_pk')
print(res)
F与Q查询
1.现在Book表里面添加两个字段并发送数据库迁移命令
'''后续添加两个字段'''
kucun = models.IntegerField(verbose_name='库存数',default=1000) # 设置默认值
maichu = models.IntegerField(verbose_name='卖出数',null=True) # 允许为空
打开pycharm终端:python38 manage.py makemigrations
python38 manage.py migrate
# 1.查询库存数大于卖出数的书籍
'''查询条件不是明确的 也需要从数据库中获取 就需要使用F查询
F:获取表中指定字段对应的数据作为查询条件
'''
from django.db.models import F
res = models.Book.objects.filter(kucun__gt=F('maichu'))
print(res)
# 2.将所有书的价格涨800块(了解)
models.Book.objects.update(price=F('price') + 800)
# 3.将所有书的名称后面追加新款(了解)
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'),Value('新款')))
# 查询主键是1或者价格大于1500的书籍
'''Q:可以将多个查询条件的关系做修改'''
res = models.Book.objects.filter(pk=1,price__gt=1500) # 逗号默认是and关系,所以查不出来
from django.db.models import Q
res = models.Book.objects.filter(Q(pk=1),Q(price__gt=1500)) # 逗号是and关系
res = models.Book.objects.filter(Q(pk=1)|Q(price__gt=1500)) # !是or关系
res = models.Book.objects.filter(~Q(pk=1)|Q(price__gt=1500)) #~取其中一个条件反,获取主键不是1的或者是价格大于1500的
print(res)
print(res.query)
#Q查询进阶操作
q_obj= Q() #1.产生q对象
q_obj.connector='or' # 默认多个条件的连接是and,可以修改为or
q_obj.children.append(('pk',1)) # 添加查询条件
q_obj.children.append(('price__gt',200)) #支持添加多个
res = models.Book.objects.filter(q_obj) #查询支持直接写q对象
for book in res:
print(book.title)
book书籍手动录入一点数据:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗