26.9 Django书籍管理练习

2024-08-03_092752

1. 搭建环境

1.1 创建数据库

Django本身不会自动创建数据库服务器或数据库实例, 这一步需要手动完成.
可以使用Navicat可视化工具或者命令行创建'library'数据库, 编码格式为utf8_mp4.
# 连接数据库
mysql -h localhost -P 3306 -u root -p123456

# 创建library数据库并设置编码
create database library character set utf8mb4;

# 查看MySQL的编码(utf8mb4)
show variables like 'character_set_database';

# 查看library数据库的编码(utf8mb4)
show create database library;

image-20240725135432937

1.2 创建Django项目

使用Pychamr工具创建Django项目.
项目名称: MyDjango .
应用名称: library .

image-20240725140020124

1.3 连接MySQL数据库

编辑Django的settings.py配置文件, 使用MySQL数据库, 配置信息如下:
# 数据库配置
DATABASES = {
    'default': {
        # 使用mysql数据库
        'ENGINE': 'django.db.backends.mysql',
        # ip地址
        'HOST': '127.0.0.1',
        # 端口号
        'POST': 3306,
        # 登入用户
        'USER': 'root',
        # 登入密码
        'PASSWORD': '123456',
        # 连接的库
        'NAME': 'library',
        # 设置编码
        'CHARSET': 'utf8mb4'
    }
}

image-20240725140557401

1.4 创建表模型

* 1. 作者表(主表)一对一作者详情表(次表), 外键建立在作者详情表中名为author.
* 2. 出版社()一对多书籍表(), 外键字段建立在多的一方书籍表中名为publish.
* 3. 作者表多对多书籍表, 外键字段建立在查询频率高的一方书籍表中名为author.
1.4.1 作者表
作者表拥有id, name, age三个字段.
# library/models.py
from django.db import models


# 作者表模型
class Author(models.Model):
    # id字段(自动添加)
    name = models.CharField(max_length=12, verbose_name='作者名称')
    age = models.IntegerField(verbose_name='作者年龄')

    def __str__(self):
        return f'{self.name}'
    

image-20240725185152671

1.4.2 作者详情表
作者详情表拥有id, phone, addr, author_id四个字段.
# 作者详情表模型
class AuthorDetail(models.Model):
    # id字段(自动添加)
    phone = models.CharField(max_length=11, verbose_name='手机号码')
    addr = models.CharField(max_length=32, verbose_name='作者住址')
    # 外键字段(一对一关联作者表实例, 并设置级联删除)
    author = models.OneToOneField(to='Author', on_delete=models.CASCADE,
                                  related_name='author_detail', verbose_name='作者名称')

    def __str__(self):
        return f'{self.id}'

image-20240725185232084

1.4.3 出版社表
出版社表拥有id, name, email, 三个字段.
# 出版社表模型
class Publish(models.Model):
    # id字段(自动添加)
    name = models.CharField(max_length=12, verbose_name='出版社名称')
    addr = models.CharField(max_length=32, verbose_name='出版社地址')
    email = models.EmailField(verbose_name='出版社邮箱')

    def __str__(self):
        return f'{self.name}'

image-20240725185255202

1.4.4 书籍表模型
书籍表表拥有id, title, price, publication_date, publish, author六个字段.
# 书籍表模型
class Book(models.Model):
    # id字段(自动添加)
    title = models.CharField(max_length=32, verbose_name='书籍名称')
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='书籍价格')
    publication_date = models.DateField(auto_now_add=True, verbose_name='书籍发布日期')
    # 外键字段
    # 一对多出版社
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, verbose_name='出版社名称')
    # 多对多作者表
    author = models.ManyToManyField(to=Author, verbose_name='作者列表')

    def __str__(self):
        return f'{self.title}'

image-20240725185359669

书籍作者关联表会自动创建有id. book_id, author_id三个字段.
1.4.5 完整代码
from django.db import models


# 作者表模型
class Author(models.Model):
    # id字段(自动添加)
    name = models.CharField(max_length=12, verbose_name='作者名称')
    age = models.IntegerField(verbose_name='作者年龄')

    def __str__(self):
        return f'{self.name}'


# 作者详情表模型
class AuthorDetail(models.Model):
    # id字段(自动添加)
    phone = models.CharField(max_length=11, verbose_name='手机号码')
    addr = models.CharField(max_length=32, verbose_name='作者住址')
    # 外键字段(一对一关联作者表实例, 并设置级联删除)
    author = models.OneToOneField(to='Author', on_delete=models.CASCADE,
                                  related_name='author_detail', verbose_name='作者名称')

    def __str__(self):
        return f'{self.id}'


# 出版社表模型
class Publish(models.Model):
    # id字段(自动添加)
    name = models.CharField(max_length=12, verbose_name='出版社名称')
    addr = models.CharField(max_length=32, verbose_name='出版社地址')
    email = models.EmailField(verbose_name='出版社邮箱')

    def __str__(self):
        return f'{self.name}'


# 书籍表模型
class Book(models.Model):
    # id字段(自动添加)
    title = models.CharField(max_length=32, verbose_name='书籍名称')
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='书籍价格')
    publication_date = models.DateField(auto_now_add=True, verbose_name='书籍发布日期')
    # 外键字段
    # 一对多出版社
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, verbose_name='出版社名称')
    # 多对多作者表
    author = models.ManyToManyField(to=Author, verbose_name='作者列表')

    def __str__(self):
        return f'{self.title}'

1.4.6 数据库迁移
# 在终端执行数据库迁移命令
PS D:\MyDjango> python manage.py makemigrations 
Migrations for 'library':
  library\migrations\0001_initial.py
    - Create model Author
    - Create model Publish
    - Create model Book
    - Create model AuthorDetail
    # 不知道为什么这里没有添加外键的提示???
PS D:\MyDjango> python manage.py migrate
...

image-20240725184054690

使用Navicat工具查询创建的表格.

image-20240725185843200

逆向数据库到模型.

image-20240725185754786

1.5 创建表记录

在Django ORM中, 执行数据库操作时, 通常不会直接返回一个'成功'的消息, 而是通过返回的结果或是否抛出异常来判断操作是否成功.
1.5.1 作者表记录
idnameage
1aa18
2bb19
3cc20
import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "MyDjango.settings")
    import django

    django.setup()

    from library.models import Author

    # 创建作者列表
    author_list = [
        Author(name='aa', age=18),
        Author(name='bb', age=19),
        Author(name='cc', age=20),
    ]

    # 批量创建作者实例
    res = Author.objects.bulk_create(author_list)
    print(res)

image-20240725211009120

image-20240726001916918

1.5.2 作者详情表记录
idphoneaddrauthor_id
1111北京1
2222上海2
3333深圳3
import os

if __name__ == "__main__":
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MyDjango.settings')
    import django

    django.setup()

    from library.models import AuthorDetail

    # 创建作者详情列表, 这里外键字段直接使用id值.
    author_detail_list = [
        AuthorDetail(phone='111', addr='北京', author_id=1),
        AuthorDetail(phone='222', addr='上海', author_id=2),
        AuthorDetail(phone='333', addr='深圳', author_id=3),
    ]

    # 批量创建作者详细实例
    res = AuthorDetail.objects.bulk_create(author_detail_list)
    print(res)

image-20240725202323335

image-20240726002115705

在Django中, bulk_create方法用于批量创建对象, 这样可以减少数据库操作次数, 提高性能.
但是, 使用bulk_create方法创建对象时, 这些对象在Python层面的实例并不会立即获得数据库中的ID.
这是因为bulk_create方法并不会立即对这些对象进行数据库查询以获取它们的ID.
1.5.3 出版社表记录
idnameaddremail
1北京出版社北京bj@qq.com
2上海出版社上海sh@qq.com
3深圳出版社深圳sz@qq.com
import os

if __name__ == "__main__":
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MyDjango.settings')
    import django

    django.setup()

    from library.models import Publish

    # 创建出版社实例列表
    publish_list = [
        Publish(name='北京出版社', addr='北京', email='bj@qq.com'),
        Publish(name='上海出版社', addr='上海', email='sh@qq.com'),
        Publish(name='深圳出版社', addr='深圳', email='sz@qq.com'),
    ]

    # 批量创建出版社实例
    res = Publish.objects.bulk_create(publish_list)
    print(res)

image-20240725211332466

image-20240726002010666

1.5.4 书籍表记录
idtitlepricepublication_datepublish_idauthor_id
1Python100.01自动生成1[1]
2MySQL200.02自动生成2[2]
3Linux300.03自动生成3[3]
4HTML400.04自动生成1[1 ,2]
import os

if __name__ == "__main__":
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MyDjango.settings')
    import django

    django.setup()

    from library.models import Book

    # 创建书籍实例列表
    book_list = [
        Book(title='Python', price=100.01, publish_id=1),
        Book(title='MySQL', price=200.02, publish_id=2),
        Book(title='Linux', price=300.03, publish_id=3),
        Book(title='HTML', price=400.04, publish_id=1)
    ]

    # 批量创建书籍实例
    res = Book.objects.bulk_create(book_list)
    print(res)

image-20240725224826292

image-20240726002053403

这里的对象不能使用外键, 例如:
book1 = Book(title='Python', price=100.01, publish_id=1)
book1.author.set([1])  # 视图在创建对象后使用外键设置多对多关联表的信息, 然后报错:
ValueError: "<Book: Python>" needs to have a value for field "id" before this many-to-many relationship can be used.
ValueError: "<Book: Python>" 需要先为字段 "id" 提供值, 然后才能使用此多对多关系.
这是因为Django的多对多关系是通过一个额外的表来实现的, 这个表需要存储关联的两个对象的主键.
如果一个对象还没有保存到数据库, 它就没有主键, 因此无法建立多对多关系.
1.5.5 书籍作者关联表
idbook_idauthor_id
111
222
333
441
542
在Django中, 当使用ManyToManyField字段时, Django会自动创建一个中间表来管理多对多关系.
然而, 这个中间表并没有直接暴露为一个模型, 因此不能像操作其他模型那样直接操作它.

如果需要访问或修改中间表的数据, 应该通过ManyToManyField提供的API来进行, 比如使用: add(), remove(), set()和clear()等方法.
这些方法会自动处理中间表的数据, 而不需要直接操作它.
    # 这种方式需要多次查询数据库
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.author.add(1)

    book_obj = models.Book.objects.filter(pk=2).first()
    book_obj.author.add(2)

    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.author.add(3)

    book_obj = models.Book.objects.filter(pk=4).first()
    book_obj.author.add(1, 2)
使用in查询来一次性获取多本书, 然后遍历这些书并为它们添加作者.
这样可以减少数据库查询的次数, 从而提高效率.
import os

if __name__ == "__main__":
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MyDjango.settings')
    import django

    django.setup()

    from library.models import Book

    # 执行一次查询
    books = Book.objects.filter(pk__in=[1, 2, 3, 4])
    for book in books:
        if book.pk == 1:
            res = book.author.add(1)  # 第一本书添加ID为1的作者
            print(res)
        elif book.pk == 2:
            res = book.author.add(2)  # 第二本书添加ID为2的作者
            print(res)
        elif book.pk == 3:
            res = book.author.add(3)  # 第三本书添加ID为3的作者
            print(res)
        elif book.pk == 4:
            res = book.author.add(1, 2)  # 第四本书添加ID为1, 2的作者
            print(res)
	

image-20240726001049820

image-20240726002029117

1.6 静态文件配置

项目中需要使用到jQuery(3.7.1)框架与bootstrap(3.3.7)框架.

jQuery文件下载地址: https://code.jquery.com/jquery-3.7.1.min.js , 将代码复制到本地文件.
bootstrap框架下载地址: https://v3.bootcss.com/getting-started/#download .
静态文件配置步骤:
* 1. 项目目录下创建static目录.
* 2. settings.py配置文件中开放静态文件的路径.
STATIC_URL = '/static/'
# 静态文件目录
STATICFILES_DIRS = [
    BASE_DIR / 'static'
]

image-20240725190936399

* 3. static目录下创建js目录, 复制jQuery文件到js目录中.
* 4. 复制bootstrap到static目录下.

image-20240725192959807

2. 主页

* 1. 在项目配置目录的路由文件中设置路由分发.
* 2. 在应用的目录下创建子路由文件.
* 3. 编写路由与视图函数的对应关系.
* 4. 在视图层中写视图函数处理请求.
* 5. 编写视图函数放回的主页面.

2.1 路由层

在项目配置目录的路由文件中设置路由分发.
# MyDjango/MyDjango/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    # 设置子路由
    path('', include(('library.urls', 'library'), namespace='library'))
]

image-20240801131230358

在应用的目录下创建子路由文件并编写路由与视图函数的对应关系, 主页面使用根目录(/), 也就是: http://127.0.0.1:8000/ . 
# library/urls.py
from django.urls import path
from library.views import home

urlpatterns = [
    # 主页面
    path('', home, name='library')
]

image-20240801134409768

2.2 视图层

主页面视图返回一个home主页面.
# library/view.py
from django.shortcuts import render


# 主页面视图
def home(request):
    return render(request, 'home.html', locals())

image-20240801134435469

2.3 模板层

2.3.1 模板页面
1. 在templates目录下创建base模板页面.
2. 在页面中导入jQuery与bootstrap文件.
3. 在页面中划分css, html, js三个块, 用于模板继承.
4. 在templates目录下创建home页面并继承base模板页面.
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>图书管理系统</title>
    <!-- 加载静态文件-->
    {% load static %}
    <script src="{% static 'js/jquery.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>

    <!-- css局部修改区域 -->
    {% block css %}

    {% endblock %}
</head>
<!-- 使用宽松布局 -->
<body class="container-fluid">
<!-- html局部修改区域 -->
{% block html %}

{% endblock %}

<!-- js局部修改区域 -->
{% block js %}

{% endblock %}
</body>
</html>

image-20240801233941369

<!-- templates/home.html -->
{% extends "base.html" %}

image-20240801201216571

2.3.2 导航栏
在base模板页面中添加一个导航条, 代码如下(这里只是为了装饰页面, 大部分功能没有实现):
<!-- 导航条, bg-info蓝色背景 -->
<nav class="navbar bg-info">
    <!-- 全宽布局, 导航栏的元素排列间隙大 -->
    <div class="container-fluid">
        <!-- 导航栏标题 -->
        <div class="navbar-header">
            <!-- 导航栏默认是折叠的. 点击这个按钮时, 会移除collapsed类, 表示导航栏已展开. -->
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <!-- 这三个span元素通过CSS样式被渲染成三条横线, 用于在小屏幕设备上表示导航栏的展开/收起状态. -->
                <span class="sr-only">切换导航</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">图书馆</a>
        </div>

        <!-- 导航栏内容, 包裹了导航栏中应该在较小屏幕设备上折叠起来的所有内容, 通过带有navbar-toggler类的按钮控制 -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <!-- 下拉列表 -->
            <ul class="nav navbar-nav">
                <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="#">Python</a></li>
                        <li><a href="#">MySQL</a></li>
                        <li><a href="#">Linux</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="#">收藏本页面</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>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>

2024-08-01_234105

启动项目, 访问: http://127.0.0.1:8000/ , 目前的主页面效果如下:

image-20240801234221374

2.3.3 页面内容
* 1. 页面内容使用栅格布局3-9.
* 2. 栅格左边部分存放一个功能选项侧边栏并存放多个功能选项, 还实现点击列表项时使其高亮显示.
* 3. 栅格右边部分放一个功能展示面板, 面板含有面板标题和面板内容两个区域, 将面板内容作为可替换区域.
<!-- 页面内容使用3-9布局 -->
<div class="row">

    <!-- 功能选项侧边栏 -->
    <div class="col-md-3">
        <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-9">
        <div class="panel panel-primary">
            <!-- 标题-->
            <div class="panel-heading">
                <h3 class="panel-title">
                    <!-- 面板标题修改区域 -->
                    {% block panel_title %}

                    {% endblock %}
                </h3>
            </div>
            <!-- 内容-->
            <div class="panel-body">
                <!--  面板主题修改区域 -->
                {% block panel_body %}

                {% endblock %}
            </div>
        </div>
    </div>

</div>

202408020837434

这段代码的主要作用是在网页上创建一个带有响应式布局的侧边栏或导航栏, 用于引导用户访问网站的不同部分或功能.
其中包含了五个链接选项: '首页', '图书列表', '出版社列表', '作者列表''更多'.
而内容展示区则通过面板来展示链接选项的内容.
启动项目, 访问: http://127.0.0.1:8000/ , 目前的主页面效果如下:

202408012345067

为列表项添加模板标签, 根据请求的URL来决定哪个列表项应该被高亮(active类为激活状态有高亮效果, disabled为禁用状态不可选中).
<div class="list-group">
    <a href="/"
       class="list-group-item {% if request.path  == '/' %} active {% endif %}">首页</a>
    <a href=""
       class="list-group-item {% if request.path == '/book_list' %} active {% endif %}">书籍列表</a>
    <a href="#"
       class="list-group-item {% if request.path == '/publish_list' %} active {% endif %}">出版社列表</a>
    <a href="#"
       class="list-group-item {% if request.path == '/author_list' %} active {% endif %}">作者列表</a>
    <a href="#" class="list-group-item disabled">更多</a>
</div>

202408021533856

启动项目, 访问主页: 127.0.0.1:8000 , 查看高亮效果:

202408021540365

列表项的a标签通过反向解析生成url, 必须先在路由层创建好路由, 模板层中才能使用, 否则会报错.
还没有填写url地址的列表项还不能点击.
2.3.4 主页面板
主页面面板是访问主页面默认展示的信息.
在home页面中继承base.html页面, 并重新定义css, panel_title, panel_body三个块的内容.
在css块中自定义一个响应式类, 确保图片在不同屏幕尺寸下保持良好的布局和显示.
在panel_title块中定义面板的名称.
在panel_body块中展示一个一个轮转图和三张缩略图.
轮转图的三张图片连接:
1. https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fs2.loli.net%2F2022%2F03%2F10%2FCNJFHvDlquibBda.jpg&pos_id=img-S9SIts4y-1722648817610)
2. https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fs2.loli.net%2F2022%2F03%2F10%2FAWzJVDgPKlc8OZt.jpg&pos_id=img-ZCuvdZg2-1722648821936)
3. https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fs2.loli.net%2F2022%2F03%2F10%2FIZlAELwBqPFjspU.jpg&pos_id=img-jb56HOte-1722648831450)

缩略图的三张图片连接:
1. https://s2.loli.net/2022/03/10/I4ziahsHbWCAkJU.jpg
2. https://s2.loli.net/2022/03/10/fFm9Lu2hkQRoJNg.jpg
3. https://s2.loli.net/2022/03/10/8ojETnzqRkLyG6h.jpg

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1646919596(1)_副本

banner1_副本 1646921174(1)_副本 1646921220(1)_副本
主页的信息就不写进去数据库了直接在html中写().
<!-- 继承模板页码 -->
{% extends "base.html" %}

<!-- 继承css块 -->
{% block css %}
    <style>
        /* 图片的响应式工具类, 它确保图片能够在不同屏幕尺寸下保持良好的布局和显示 */
        .img-fluid {
            width: 100%;
            height: auto;
        }
    </style>
{% endblock %}

<!-- 继承面板标题块 -->
{% block panel_title %}主页功能展示{% endblock %}


<!-- 继承面板主体块 -->
{% block panel_body %}
    <!-- 轮转图 -->
    <div>
        <div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
            <!-- 指标 -->
            <ol class="carousel-indicators">
                <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
                <li data-target="#carousel-example-generic" data-slide-to="1"></li>
                <li data-target="#carousel-example-generic" data-slide-to="2"></li>
            </ol>

            <!-- 幻灯片装饰器 -->
            <div class="carousel-inner " role="listbox">
                <div class="item active ">
                    <!--style="width: 100%;"是图片铺满转轮图-->
                    <img src="https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fs2.loli.net%2F2022%2F03%2F10%2FCNJFHvDlquibBda.jpg&pos_id=img-S9SIts4y-1722648817610)" alt="..." class="img-fluid">
                </div>
                <div class="item">
                    <img src="https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fs2.loli.net%2F2022%2F03%2F10%2FAWzJVDgPKlc8OZt.jpg&pos_id=img-ZCuvdZg2-1722648821936)" alt="..." class="img-fluid">
                </div>
                <div class="item">
                    <img src="https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fs2.loli.net%2F2022%2F03%2F10%2FIZlAELwBqPFjspU.jpg&pos_id=img-jb56HOte-1722648831450)" alt="..." class="img-fluid">
                </div>
            </div>

            <!-- 控制 -->
            <a class="left carousel-control" href="#carousel-example-generic" role="button"
               data-slide="prev">
                <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
                <!-- class="sr-only 这个类通常用于屏幕阅读器(Screen Readers), 它对于视觉用户是不可见的. -->
                <span class="sr-only">上一页</span>
            </a>
            <a class="right carousel-control" href="#carousel-example-generic" role="button"
               data-slide="next">
                <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
                <span class="sr-only">下一页</span>
            </a>
        </div>
    </div>
    <br>
    <!-- 缩略图 -->
    <div>
        <div class="row">
            <div class="col-sm-6 col-md-4">
                <div class="thumbnail">
                    <img src="https://s2.loli.net/2022/03/10/I4ziahsHbWCAkJU.jpg" alt="...">
                    <div class="caption">
                        <h3>Python</h3>
                        <p>由荷兰数学和计算机科学研究学会的吉多·范罗苏姆于1990年代初设计, 作为一门叫做ABC语言的替代品.
                            Python提供了高效的高级数据结构, 还能简单有效地面向对象编程...</p>
                        <p><a href="#" class="btn btn-primary" role="button">关注</a> <a
                                href="#" class="btn btn-default" role="button">收藏</a></p>
                    </div>
                </div>
            </div>

            <div class="col-sm-6 col-md-4">
                <div class="thumbnail">
                    <img src="https://s2.loli.net/2022/03/10/fFm9Lu2hkQRoJNg.jpg" alt="...">
                    <div class="caption">
                        <h3>MySQL</h3>
                        <p>是最流行的关系型数据库管理系统,
                            在WEB应用方面MySQL是最好的RDBMS(关系数据库管理系统)应用软件之一...</p>
                        <p><a href="#" class="btn btn-primary" role="button">关注</a>
                            <a href="#" class="btn btn-default" role="button">收藏</a></p>
                    </div>
                </div>
            </div>

            <div class="col-sm-6 col-md-4">
                <div class="thumbnail">
                    <img src="https://s2.loli.net/2022/03/10/8ojETnzqRkLyG6h.jpg" alt="...">
                    <div class="caption">
                        <h3>Linux</h3>
                        <p>
                            全称GNU/Linux, 是一套免费使用和自由传播的类UNIX操作系统, 它主要受到Minix和Unix思想的启发,
                            是一个基于POSIX和Unix的多用户, 多任务, 支持多线程和多CPU的操作系统...</p>
                        <p><a href="#" class="btn btn-primary" role="button">Button</a> <a
                                href="#" class="btn btn-default" role="button">Button</a></p>
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

2024-08-01_231740

启动项目, 访问: http://127.0.0.1:8000/ , 主页面效果如下:

202408012347838

3. 书籍列表

3.1 路由层

在子路由中添加一个路由, 当用户访问: 127.0.0.1:8000/book_list时展示书籍信息.
# library/urls.py
from django.urls import path
from library.views import home, book_list

urlpatterns = [
    # 主页面
    path('', home, name='library'),
    # 书籍列表
    path('book_list', book_list, name='book_list')
]

image-20240801235917702

3.2 视图层

视图函数中读取数据库中的所有的书籍实例并返回给book_list页面(现在数量量少, 先不考虑分页的问题).
# library/views.py
from django.shortcuts import render
# 导入模型
from library.models import Book


# 书籍列表
def book_list(request):
    # 读取所有书籍实例
    books = Book.objects.all()
    # 返回页面与书籍实例
    return render(request, 'book_list.html', locals())

image-20240802045118133

3.3 模板层

书籍列表面板展示所有书籍的信息.
1. 在侧边栏的书籍列表中绑定访问地址为.
2. 在templates目录下创建book_list.html页面.
3. 在书籍列表页面中继承base.html页面, 并重新定义css, panel_title, panel_body三个块的内容.
4. 在css块中自定义一个类, 让表格的每行文字垂直对齐(默认向上对齐不怎么好看).
5. 在panel_title块中定义面板的名称.
6. 在panel_body块中使用表格展示书籍信息, 表单展示的信息如下:
序号编号书名作者出版社出版日期价格操作按钮
1书籍id号唯一可能有多个唯一唯一唯一编辑 删除
<!-- 在base.html中为图书列表选项的绑定url地址 -->
<a href="{% url 'library:book_list' %}"
   class="list-group-item {% if request.path == '/book_list' %} active {% endif %}">书籍列表</a>

202408021543960

<!-- templates/book_list.html -->
<!-- 继承模板页码 -->
{% extends "base.html" %}

{% block css %}
    <style>
        .table > tbody > tr > td {
            vertical-align: middle;
        <!-- 书籍信息在表格中垂直对齐 -->
        }
    </style>

{% endblock %}
<!-- 继承面板标题块 -->
{% block panel_title %}书籍信息{% endblock %}

<!-- 继承面板主体块 -->
{% block panel_body %}
    <!-- 表单. table-striped表单条纹, table-hover:鼠标悬停效果-->
    <table class="table table-striped table-hover">
        <thead>
        <tr>
            <th>序号</th>
            <th>编号</th>
            <th>书名</th>
            <th>作者</th>
            <th>出版社</th>
            <th>出版时间</th>
            <th>价格</th>
            <th>操作</th>
        </tr>
        </thead>
        <!-- 遍历书籍实例 -->
        <tbody>
        {% for book in  books %}
            <tr>
                <td>{{ forloop.counter }}</td>
                <td>{{ book.pk }}</td>
                <td>{{ book.title }}</td>
                <td>
                    {% for author_obj in book.author.all %}
                        <!--每个遍历循环都有一个forloop对象-->
                        {% if forloop.last %}  <!-- 判断当前的作者对象是否为最后一个遍历的元素 --->
                            {{ author_obj.name }} 
                        {% else %}
                            {{ author_obj.name }},
                        {% endif %}
                    {% endfor %}
                </td>
                <td>{{ book.publish.name }}</td>
                <td>{{ book.publication_date|date:'Y-m-d' }}</td>
                <td>{{ book.price }}</td>
                <td>
                    <a href="" class="btn btn-primary btn-sm">编辑</a>
                    <a href="" class="btn btn-danger btn-sm">删除</a>
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
{% endblock %}

image-20240802071956402

启动项目, 访问: http://127.0.0.1:8000/book_list , 书籍列表页面效果如下:

image-20240802160902891

4. 添加书籍

4.1 路由层

在子路由中添加一个路由, 当用户访问: 127.0.0.1:8000/book_add时展示书籍添加页面.
# library/urls.py
from django.urls import path
from library.views import home, book_list, book_add

urlpatterns = [
    # 主页面
    path('', home, name='library'),
    # 书籍列表
    path('book_list', book_list, name='book_list'),
    # 书籍添加
    path('book_add', book_add, name='book_add')
]

image-20240802202743606

4.2 视图层

先分析添加书籍表单中需要的信息:
* 1. id字段 , 自增不用管.
* 2. title书名字段, 需要表单提供. 
* 3. price书籍价格字段, 需要表单提供.                     
* 4. publication_date出版时间, 自动添加不用管.  
* 5. 外键字段publish, 使用已经存在的出版社.
* 6. 外键字段author, 使用已经存在的作者.
视图需要读取数据库中的作者表和出版社表的数据再与添加图书页面一同返回.
# library/views.py
from django.shortcuts import render
# 导入模型
from library.models import Book, Publish, Author


def book_add(request):
    # 获取所有的出版社实例
    publishing = Publish.objects.all()
    # 获取所有的作者实例
    authors = Author.objects.all()

    return render(request, 'book_add.html', locals())

image-20240802162522633

4.3 模板层

1. 在base.html模板页面的侧边栏中添加一个列表项.
2. 在templates目录下创建book_add.html页面.
3. 在添加书籍页面中继承base.html页面, 并重新定义panel_title, panel_body两个块的内容.
4. 在panel_title块中定义面板的名称.
5. 在panel_body块中添加一个表单用于收集书籍信息, 收集的信息如下:
* 1. id字段 , 自增不用管.
* 2. title书名字段, 需要表单提供, 使用input输入框, 名称为: title.
* 3. price书籍价格字段, 需要表单提供, 使用input输入框, 名称为: price.               
* 4. publication_date出版时间字段, 自动添加(数据插入的时间).
* 5. 外键字段publish, 使用已经存在的出版社, 使用下拉框, 名称为: publish_id, 并设置单选, 展示的是出版社的名称提交的是出版社id. 
* 6. 外键字段author, 使用已经存在的作者, 使用下拉框, 名称为: publish_id, 并设置多选, 展示的是作者的名称提交的是作者id. 
<!-- base.html的侧边栏中添加一个列表项 -->
<a href="{% url 'library:book_add' %}"
   class="list-group-item {% if request.path == '/book_add' %} active {% endif %}">添加书籍</a>

2024-08-02_195959

<!-- templates/book_add.html -->

<!-- 继承模板页码 -->
{% extends "base.html" %}

<!-- 继承面板标题块 -->
{% block panel_title %} 书籍添加 {% endblock %}

<!-- 继承面板主体块 -->
{% block panel_body %}

    <!-- 表单 -->
    <form action="" method="post">
        <div class="input-group">
            <span class="input-group-addon">书名:</span>
            <input type="text" class="form-control mt-5" placeholder="书籍名称" name="title">
        </div>
        <br>

        <div class="input-group">
            <span class="input-group-addon">价格:</span>
            <input type="text" class="form-control" placeholder="书籍价格" name="price">
        </div>
        <br>

        <div class="input-group">
            <span class="input-group-addon">出版社:</span>
            <select name="publish_id" class="form-control">
                <!--selected默认选中, disabled不可手动选中, hidden隐藏选项-->
                <option selected disabled hidden>点击选择</option>
                {% for publish in publishing %}
                    <option value="{{ publish.pk }}">{{ publish.name }}</option>
                {% endfor %}

            </select>
        </div>
        <br>

        <div class="input-group">
            <span class="input-group-addon">作者:</span>
            <select name="author_id" multiple="multiple" class="form-control">
                {% for author in authors %}
                    <option value="{{ author.pk }}">{{ author.name }}</option>
                {% endfor %}
            </select>
        </div>
        <br>

        <p>
            <input type="submit" value="添加" class="btn btn-primary form-control">
        </p>
    </form>
    
{% endblock %}

image-20240802215110434

启动项目, 访问: http://127.0.0.1:8000/book_add , 查看添加书籍页面的效果:

image-20240802204810661

4.4 写入数据

1. 修改视图函数, 从POST请求提交的表单中获取书籍信息.
2. 需要为两张表写入数据(表单中提交的数据都是字符串, 在写入数据库的时候会自动转换类型.)
   首先, 往书籍表中写数据(需要为title, price, publish_id三个字段提供值).
   最后, 使用书籍对象的author外键字段获取到书籍作者关联表, 再使用set()方法添加多对多关联.
   set()方法允许传递一个可迭代对象(比如列表, 元组等), 它会遍历这个可迭代对象, 并逐个添加其中的元素.
3. 写入成功后重定向到书籍列表页面.
# library/views.py
from django.shortcuts import render, redirect
# 导入模型
from library.models import Book, Publish, Author


# 添加书籍
def book_add(request):
    # 处理GET请求:
    if request.method == 'GET':
        # 获取所有的出版社实例
        publishing = Publish.objects.all()
        # 获取所有的作者实例
        authors = Author.objects.all()

        return render(request, 'book_add.html', locals())

    # 处理POST请求:
    if request.method == 'POST':
        # 获取书籍信息
        title = request.POST.get('title')
        price = request.POST.get('price')
        publish_id = request.POST.get('publish_id')
        # 作者id有多个, 通过getlist方法获取, 值是一个列表
        author_id = request.POST.getlist('author_id')

        # 数据校验跳过
        # print(title, price, publish_id, author_id)

        # 创建书籍实例
        book = Book.objects.create(title=title, price=price, publish_id=publish_id)
        # # 添加多对多关联
        book.author.set(author_id)
        # 重定向到书籍列表页面
        return redirect('/book_list')

image-20240803000628196

启动项目, 访问: http://127.0.0.1:8000/book_add , 填写信息并提交.

image-20240802214926019

实例添加成功之后, 会跳转到书籍列表页面, 可以查看新添加的书籍记录.

image-20240802215440597

5. 书籍修改

编辑书籍信息的思路:
* 1. 给编辑按钮绑定请求地址, 在书籍列表中点击修改时, 提交一个get请求, 并携带书籍的id.
* 2. 路由中使用转换器获取id值并传递给视图函数.
* 3. 后端处理请求, 并通过书籍id获取对应书籍实例数据, 返回一个书籍修改页面和需要修改的书籍实例.
* 4. 书籍修改页面中展示一个表单, 表单中展示可修改的书籍信息.

5.1 路由层

在子路由中添加一个路由, 当用户访问: 127.0.0.1:8000/book_set/id时展示书籍修改页面.
请求地址携带了书籍id, 可以通过内置的转换器来捕获获取这个值.
# library/urls.py
from django.urls import path
from library.views import home, book_list, book_add, book_set

urlpatterns = [
    # 主页面
    path('', home, name='library'),
    # 书籍列表
    path('book_list', book_list, name='book_list'),
    # 书籍添加
    path('book_add', book_add, name='book_add'),
    # 书籍修改
    path('book_set/<int:book_id>', book_set, name='book_set')
]

image-20240802223059777

5.2 视图层

修改数据页面需要获取所有的出版社信息和作者信息和修改的书籍信息, 在视图中获取并返回.
# library/views.py

# 书籍修改, 需要接收内置转换器传递的参数
def book_set(request, book_id):
    # 处理GET请求:
    if request.method == 'GET':
        # 获取所有的出版社实例
        publishing = Publish.objects.all()
        # 获取所有的作者实例
        authors = Author.objects.all()
        # 获取书籍实例
        book = Book.objects.filter(id=book_id).first()
        return render(request, 'book_set.html', locals())

image-20240802233425232

5.3 模板层

在book_list.html页面中为编辑按钮绑定跳转地址, 提交的get请求携带书籍实例的id.
<!-- templates/book_list.html -->
<!-- book.pk前面不需要添加/url模板标签会自动处理URL的生成, 只需要提供正确的参数即可,  -->
<a href="{% url 'library:book_set'  book.pk %}" class="btn btn-primary btn-sm">编辑</a>

2024-08-02_234615

在book_set.html页面中展示书籍实例的信息, 通过{% if %}标签可以对外键字段进行默认选中.
<!-- templates/book_set.html -->
<!-- 继承模板页码 -->
{% extends "base.html" %}

<!-- 继承面板标题块 -->
{% block panel_title %} 书籍信息修改 {% endblock %}

<!-- 继承面板主体块 -->
{% block panel_body %}
    <!-- 表单  -->
    <form action="" method="post">
        {% csrf_token %}
        <div class="input-group">
            <span class="input-group-addon">书名:</span>
            <input type="text" class="form-control mt-5" placeholder="书籍名称" name="title" value="{{ book.title }}">
        </div>
        <br>

        <div class="input-group">
            <span class="input-group-addon">价格:</span>
            <input type="text" class="form-control" placeholder="书籍价格" name="price" value="{{ book.price }}">
        </div>
        <br>

        <div class="input-group">
            <span class="input-group-addon">出版社:</span>
            <select name="publish_id" class="form-control">
                {% for publish in publishing %}
                    <!-- 如果出版社id是书籍实例的外键则默认选中 -->
                    {% if publish.id == book.publish_id %}
                        <option selected value="{{ publish.pk }}">{{ publish.name }}</option>
                    {% else %}
                        <option value="{{ publish.pk }}">{{ publish.name }}</option>
                    {% endif %}
                {% endfor %}

            </select>
        </div>
        <br>

        <div class="input-group">
            <span class="input-group-addon">作者:</span>
            <select name="author_id" multiple="multiple" class="form-control">
                {% for author in authors %}
                    <!--all()获取的是queryset对象, 判断author实例是否在queryset对象中, 如果中则默认选中-->
                    {% if author in book.author.all %}
                        <option selected value="{{ author.pk }}">{{ author.name }}</option>
                    {% else %}
                        <option value="{{ author.pk }}">{{ author.name }}</option>
                    {% endif %}
                {% endfor %}
            </select>
        </div>
        <br>

        <p>
            <input type="submit" value="提交" class="btn btn-primary form-control">
        </p>
    </form>

{% endblock %}

image-20240802234825192

启动项目, 访问: 127.0.0.1:8000/book_list , 选中一书籍并点击编辑按钮.

image-20240802235034947

点击编辑后访问: 127.0.0.1:8000/book_set/4 , 书籍修改页面展示如下:

5.4 修改数据

1. 修改视图函数, 从POST请求提交的表单中获取书籍信息.
2. 需要为两张表更新数据(表单中提交的数据都是字符串, 在写入数据库的时候会自动转换类型.)
   首先, 获取书籍对象, 使用update更新字段: title, price, publish_id的值. 
   最后, 使用书籍对象的author外键字段获取到书籍作者关联表, 再使用set()方法修改多对多关联.
   set()方法允许传递一个可迭代对象(比如列表, 元组等), 它会遍历这个可迭代对象, 并逐个添加其中的元素.
3. 修改成功后重定向到书籍列表页面.
# library/views.py

# 书籍修改, 需要接收内置转换器传递的参数
def book_set(request, book_id):
    # 处理GET请求:
    if request.method == 'GET':
        # 获取所有的出版社实例
        publishing = Publish.objects.all()
        # 获取所有的作者实例
        authors = Author.objects.all()
        # 获取书籍实例
        book = Book.objects.filter(id=book_id).first()
        return render(request, 'book_set.html', locals())

    # 处理POST请求:
    if request.method == 'POST':
        # 获取书籍信息
        title = request.POST.get('title')
        price = request.POST.get('price')
        publish_id = request.POST.get('publish_id')
        # 作者id有多个, 通过getlist方法获取, 值是一个列表
        author_id = request.POST.getlist('author_id')

        # 数据校验跳过
        # print(title, price, publish_id, author_id)

        # 获取书籍实例, update是queryset对象方法
        query_set = Book.objects.filter(id=book_id)
        # 更新书籍实例
        query_set.update(title=title, price=price, publish_id=publish_id)
        # 通过first()方法获取书籍实例, 在通过author外键更新多对多关联
        query_set.first().author.set(author_id)
        # 重定向到书籍列表页面
        return redirect('/book_list')

image-20240803011553905

启动项目, 访问两次: 127.0.0.1:8000/book_list/4 , 获取书籍id为4的修改页面, 一个做参考一个做修改并提交请求.

image-20240803011008594

修改成功之后, 跳转到书籍列表页面中, 查看修改后的书籍信息.

image-20240803011518363

访问注点意:
修改页面的路由是: 'book_set/<int:book_id>' .
视图函数的定义为: def book_set(request, book_id) .
点击编辑的时候, 访问book_set页面需要携带一个id值为路径, 视图函数也始终接收被转换器捕获的id值路径.
例如访问: 127.0.0.1:8000/book_list/4 , 返回一个书籍修改页面.
当修改信息完成后, 表单向当前的url地址(127.0.0.1:8000/book_list/4)提交POST请求.
上诉过程中, 请求地址中一直确保着book_id, 否则路由或book_set视图函数会出错.

6. 删除书籍

删除书籍信息的思路:
* 1. 给删除按钮绑定请求地址, 在书籍列表中点击删除时, 提交一个get请求, 并携带书籍的id.
* 2. 路由中使用转换器获取id值并传递给视图函数.
* 3. 后端处理请求, 并通过书籍id删除对应书籍实例数据.
* 4. 删除数据后跳转到图书列表中.

6.1 路由层

在子路由中添加一个路由, 当用户访问: 127.0.0.1:8000/book_delete/id时展示书籍修改页面.
请求地址携带了书籍id, 可以通过内置的转换器来捕获获取这个值.
# library/urls.py

from django.urls import path
from library.views import home, book_list, book_add, book_set, book_delete

urlpatterns = [
    # 主页面
    path('', home, name='library'),
    # 书籍列表
    path('book_list', book_list, name='book_list'),
    # 书籍添加
    path('book_add', book_add, name='book_add'),
    # 书籍修改
    path('book_set/<int:book_id>', book_set, name='book_set'),
    # 书籍删除
    path('book_delete/<int:book_id>', book_delete, name='book_delete'),
]

image-20240803004322902

6.2 视图层

在视图直接通过书籍id删除实例, 然后重定向到书籍列表页面.
# library/views.py

# 书籍删除
def book_delete(request, book_id):
    # 通过书籍主键删除实例
    Book.objects.filter(id=book_id).delete()
    # 重定向到书籍列表页面
    return redirect('/book_list')

image-20240803011904961

6.3 模型层

在book_list.html页面中为删除按钮绑定跳转地址, 提交的get请求携带书籍实例的id.
<!-- templates/book_list.html -->

<a href="{% url 'library:book_delete'  book.pk %}" class="btn btn-danger btn-sm">删除</a>

2024-08-03_005157

启动项目, 访问: 127.0.0.1:8000/book_list, 选中一个书籍实例并点击删除.

image-20240803011716679

删除成功之后, 跳转到书籍列表页面中, 查看删除后的书籍信息.

image-20240803011741742

7. 页面背景

在模板页面中写写一个js替换body的背景颜色.

<!-- 背景色修改 -->
<script>
    $(document).ready(function () {
        // 监听下拉框中链接的点击事件
        $('.dropdown-menu a').click(function (e) {
            e.preventDefault(); // 阻止链接的默认跳转行为
            // 移除body上已有的背景类(如果有的话)
            var $body = $('body');
            $body.removeClass('bg-success bg-danger bg-warning');
            // 根据点击的链接文本添加相应的背景类
            var text = $(this).text();
            switch (text) {
                case '浅绿色':
                    $body.addClass('bg-success');
                    break;
                case '浅粉色':
                    $body.addClass('bg-danger');
                    break;
                case '浅黄色':
                    $body.addClass('bg-warning');
                    break;
            }
        });
    });
</script>

image-20240803014842185

启动项目, 访问任意一个页面, : 127.0.0.1:8000/book_list , 在导航栏中设置页面的背景颜色, 刷新页面则恢复默认的白色.

GIF 2024-8-3 1-51-24

出版社列表与作者表有兴趣自己去完善.
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Django 是一个优秀的 Python Web 框架,可以用它来开发各种 Web 应用,包括图书管理系统。下面是一个简单的 Django 图书管理系统的示例: 1. 创建 Django 项目和应用 首先安装 Django,然后创建一个 Django 项目和一个应用: ``` $ django-admin startproject library $ cd library $ python manage.py startapp books ``` 2. 创建模型 在应用的 models.py 文件中定义图书和作者两个模型: ```python from django.db import models class Author(models.Model): name = models.CharField(max_length=50) email = models.EmailField() def __str__(self): return self.name class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.CharField(max_length=50) publication_date = models.DateField() def __str__(self): return self.title ``` 3. 创建视图 在应用的 views.py 文件中定义视图函数: ```python from django.shortcuts import render from .models import Book def book_list(request): books = Book.objects.all() return render(request, 'books/book_list.html', {'books': books}) ``` 4. 创建 URL 映射 在应用的 urls.py 文件中定义 URL 映射: ```python from django.urls import path from .views import book_list urlpatterns = [ path('', book_list, name='book_list'), ] ``` 5. 创建模板 在应用的 templates/books 目录下创建 book_list.html 模板: ```html {% extends 'base.html' %} {% block content %} <h1>Books</h1> <ul> {% for book in books %} <li>{{ book.title }}</li> {% endfor %} </ul> {% endblock %} ``` 6. 运行应用 运行应用并访问 http://127.0.0.1:8000/books/,就可以看到所有图书的列表了: ``` $ python manage.py runserver ``` 以上是一个简单的 Django 图书管理系统的示例。实际上,在这个基础上可以添加更多的功能,比如添加、编辑和删除图书等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值