尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python Django MySQL 图书管理系统开发实战:数据建模到部署全解析

Python Django MySQL 图书管理系统开发实战:数据建模到部署全解析 简介这是一套基于Python Django框架开发的图书管理系统完整项目采用Bootstrap前端与MySQL数据库面向Web开发初学者及课程设计/毕业设计参考者解决出版社、作者、图书三类核心信息的增删改查并覆盖登录注册、权限控制、图书借阅归还与多条件查询等功能需求。压缩包共63个文件体积仅1.33MB包含13个Python源码文件、11个HTML模板、7个CSS样式、4个JavaScript脚本及多张界面截图项目按Django标准结构组织templates模板层、static静态资源层、app01业务逻辑层划分清晰迁移文件与配置齐全。已有322人学习下载可直接导入运行并二次开发。通过该项目可掌握Django MTV架构、ORM模型定义、Bootstrap响应式页面搭建及MySQL数据操作也能学习到用户登录、出版社/作者/图书管理模块的完整实现思路是一份兼顾教学与实战的优质参考资料。1. 项目背后图书管理系统的真实需求与技术选型做这套图书管理系统之前我自己也经历过一段“纸质记录 Excel 表格”混着用的时期。出版社要统计作者稿酬、库房要看图书库存、编辑要查某本书有哪些作者参与每次都要翻好几张表才能凑齐信息。痛得久了就萌生了做一个“出版社-作者-图书”一体化管理系统的想法。项目标题里已经点得很清楚Python Django Bootstrap MySQL 四个核心组件。这套组合在中小型业务系统里非常常见原因也很直白Python开发效率高学习曲线平缓做 CRUD 类业务系统非常适合Django自带 ORM、Admin 后台、表单校验和模板引擎能大幅减少重复造轮子Bootstrap帮我省去了写 CSS 的时间开箱即用的栅格系统和组件能快速撑起一个体面的前端页面MySQL作为成熟稳定的关系型数据库在数据一致性和并发处理上表现可靠和 Django ORM 的配合也足够顺畅。这个项目适合谁参考一是正在学 Django 但不知道练手项目怎么设计的初学者二是需要快速给团队或客户搭建一个带前后端交互的数据库管理系统的人。我会把从建模到部署的关键细节都拆开讲并附上我踩过的坑和验证过的最佳实践。2. 数据建模把出版社、作者、图书之间的关系理顺2.1 实体关系梳理与字段设计思路图书管理系统最核心的部分不是页面而是数据模型。三个实体中出版社Publisher一个出版社可以出版多本图书所以出版社和图书之间是一对多关系作者Author一个作者可以写多本书一本书也可以有多个作者联合署名所以作者和图书之间是多对多关系图书Book需要保存书名、ISBN、定价、出版日期等基础信息。我最终的模型设计如下# models.py from django.db import models class Publisher(models.Model): name models.CharField(max_length100, verbose_name出版社名称, uniqueTrue) address models.CharField(max_length200, verbose_name地址) city models.CharField(max_length50, verbose_name所在城市) country models.CharField(max_length50, verbose_name国家, default中国) website models.URLField(verbose_name官网, blankTrue) class Meta: verbose_name 出版社 verbose_name_plural verbose_name def __str__(self): return self.name class Author(models.Model): name models.CharField(max_length50, verbose_name姓名) email models.EmailField(verbose_name邮箱, blankTrue) birth_date models.DateField(verbose_name出生日期, nullTrue, blankTrue) phone models.CharField(max_length20, verbose_name联系电话, blankTrue) class Meta: verbose_name 作者 verbose_name_plural verbose_name def __str__(self): return self.name class Book(models.Model): title models.CharField(max_length200, verbose_name书名) ISBN models.CharField(max_length13, verbose_nameISBN, uniqueTrue) publisher models.ForeignKey(Publisher, on_deletemodels.CASCADE, verbose_name出版社, related_namebooks) authors models.ManyToManyField(Author, verbose_name作者, related_namebooks) publication_date models.DateField(verbose_name出版日期, nullTrue, blankTrue) price models.DecimalField(max_digits8, decimal_places2, verbose_name定价) stock models.PositiveIntegerField(default0, verbose_name库存) summary models.TextField(verbose_name简介, blankTrue) class Meta: verbose_name 图书 verbose_name_plural verbose_name ordering [-publication_date] # 默认按出版日期倒序 def __str__(self): return self.title2.2 几个字段设计的关键决定有些细节新手容易忽视但直接关系到后续开发是否顺手外键为什么要设置on_deletemodels.CASCADE当出版社被删除时该出版社下所有图书会同步删除避免数据库中残留无法关联的孤儿数据。如果业务上不允许删除出版社可以改成PROTECT这样删除出版社前必须先把图书清空安全性更高。多对多关系里related_name的价值不设置 related_name 时Django 会自动生成book_set类似的名称。设置之后我可以通过publisher.books.all()直接获取该出版社下的所有图书代码语义清楚查询效率也更高。ISBN 字段为什么用CharField而不用BigIntegerFieldISBN 的用途是精确匹配不需要参与数值运算而且有些旧版 ISBN 会带有连字符用字符类型更灵活。同理图书定价用DecimalField(max_digits8, decimal_places2)这是为了精确表达人民币分位比FloatField更严谨。2.3 数据迁移的规范流程模型定义完成后需要执行两步操作python manage.py makemigrations python manage.py migrate我习惯在生成迁移文件之后先用python manage.py sqlmigrate 应用名 迁移编号检查一下 SQL 语句是否和预期一致。比如想确认 Book 表有没有正确建立外键执行后就能看到明确的FOREIGN KEY约束定义。数据迁移是一个项目的版本管理一部分如果改了模型一定要生成新的迁移文件不要直接手改数据库表否则以后环境重构时模型和表结构就对不上了。3. 环境搭建与项目初始化3.1 Python 与 Django 环境准备安装 Python 本身不难但要注意版本兼容。Django 4.x/5.x 都要求 Python 3.10 及以上如果你的系统默认还是 Python 3.6/3.8建议直接用venv创建独立虚拟环境避免污染全局环境。python3 -m venv venv source venv/bin/activate # Windows 下为 venv\Scripts\activate pip install django pip install mysqlclient # 用于 Django 连接 MySQL这里插一个重要提醒mysqlclient 在某些 Linux 环境下可能装不上因为它依赖系统级的 MySQL 开发头文件。需要先执行apt install libmysqlclient-dev或yum install mysql-devel再执行 pip 安装。如果实在不想折腾系统依赖可以改用PyMySQL# 在项目 __init__.py 中加入 import pymysql pymysql.install_as_MySQLdb()这个方案兼容性很好但性能上略逊于 mysqlclient。我的建议是正式项目优先用 mysqlclient个人学习环境用 PyMySQL 会省事很多。3.2 Django 项目与应用创建django-admin startproject book_system cd book_system python manage.py startapp books创建完应用之后记得在settings.py的INSTALLED_APPS中加入books否则迁移和模板加载都会找不到应用。3.3 MySQL 数据库创建与 Django 配置先在 MySQL 中建库。注意字符集一定要设置成utf8mb4否则后续存中文书名可能报错或出现乱码。CREATE DATABASE book_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;然后在settings.py中配置数据库连接DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: book_system, USER: root, PASSWORD: 你的密码, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, }, } }配置完成后用python manage.py migrate验证连接是否成功。如果这一步报Cant connect to MySQL server优先检查 MySQL 服务是否启动以及 root 用户是否允许本机登录。这里有个常见坑MySQL 8.x 默认认证插件是caching_sha2_password而旧版 mysqlclient 不支持。解决办法是登录 MySQL 后执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;换成mysql_native_password认证方式后重启服务通常就能连上。4. 核心功能实现从列表到增删改查的完整流程4.1 路由设计与视图函数拆解Django 提倡“肥模型瘦视图”但复杂页面的视图函数也需要模块化。我为三个实体各建了一组路由结构如下# books/urls.py from django.urls import path from . import views urlpatterns [ path(, views.index, nameindex), path(books/, views.book_list, namebook_list), path(books/add/, views.book_add, namebook_add), path(books/int:pk/edit/, views.book_edit, namebook_edit), path(books/int:pk/delete/, views.book_delete, namebook_delete), path(publishers/, views.publisher_list, namepublisher_list), path(authors/, views.author_list, nameauthor_list), ]视图层我主要采用基于函数的视图FBV结合通用视图的混合写法。列表页用ListView可以少写分页逻辑但涉及多表联查和自定义筛选条件时FBV 反而更灵活。下面这段是图书列表的完整实现重点在select_related和prefetch_related的用法# books/views.py from django.shortcuts import render, get_object_or_404, redirect from django.core.paginator import Paginator from .models import Book, Publisher, Author from .forms import BookForm def book_list(request): # select_related 用于外键关联的 Publisherprefetch_related 用于多对多的 Author books Book.objects.select_related(publisher).prefetch_related(authors).all() # 按书名或 ISBN 模糊搜索 keyword request.GET.get(keyword, ) if keyword: books books.filter(title__icontainskeyword) | books.filter(ISBN__icontainskeyword) # 按出版社筛选 publisher_id request.GET.get(publisher, ) if publisher_id: books books.filter(publisher_idpublisher_id) paginator Paginator(books, 10) # 每页 10 条 page_number request.GET.get(page) page_obj paginator.get_page(page_number) return render(request, books/book_list.html, { page_obj: page_obj, publishers: Publisher.objects.all(), keyword: keyword, publisher_id: publisher_id, })select_related(publisher)会把关联的出版社数据用一条 SQL JOIN 查询出来prefetch_related(authors)则额外执行一条查询把多对多关联数据批量加载到内存。这两个方法能有效避免循环查询造成的 N1 性能问题。4.2 表单校验与数据入库表单部分我直接用 Django 的 ModelForm 定义省去手动处理 POST 参数的过程# books/forms.py from django import forms from .models import Book, Publisher, Author class BookForm(forms.ModelForm): class Meta: model Book fields [title, ISBN, publisher, authors, publication_date, price, stock, summary] widgets { publication_date: forms.DateInput(attrs{type: date}), summary: forms.Textarea(attrs{rows: 3}), }图书的新增和编辑可以复用同一套表单逻辑def book_add(request): if request.method POST: form BookForm(request.POST) if form.is_valid(): form.save() return redirect(book_list) else: form BookForm() return render(request, books/book_form.html, {form: form, page_title: 新增图书})ModelForm 的校验有个天然好处它会把模型定义时的字段约束自动映射到 HTML 表单上。比如ISBN被设置为uniqueTrue当用户输入重复的 ISBN 时校验会直接提示“已存在相同的 ISBN”不需要我手动再写一次查重逻辑。编辑功能在book_edit中通过instance参数绑定已有对象def book_edit(request, pk): book get_object_or_404(Book, pkpk) if request.method POST: form BookForm(request.POST, instancebook) if form.is_valid(): form.save() return redirect(book_list) else: form BookForm(instancebook) return render(request, books/book_form.html, {form: form, page_title: 编辑图书})4.3 删除操作与数据安全删除视图要注意 POST 请求与 CSRF 校验。直接用 GET 方式删除虽然代码简单但容易被恶意 URL 触发误删所以必须用表单提交 POST 请求并且视图里要判断request.method POST。def book_delete(request, pk): book get_object_or_404(Book, pkpk) if request.method POST: book.delete() return redirect(book_list) return render(request, books/book_confirm_delete.html, {book: book})删除对象还有一个容易忽略的点多对多关系中的关联数据不会被级联删除。比如删除一个作者时Django 会自动清空该书和作者之间的中间表记录但书本身还在。这一点和一对多外键的 CASCADE 行为不同测试删除功能时一定要确认预期行为是对的。4.4 Bootstrap 页面模板与交互优化前端我选 Bootstrap 5直接通过 CDN 引入不用下载静态文件到本地省时省力。基础模板如下!-- templates/base.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 title{% block title %}图书管理系统{% endblock %}/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/css/bootstrap.min.css relstylesheet /head body nav classnavbar navbar-expand-lg navbar-dark bg-primary div classcontainer a classnavbar-brand href{% url index %}图书管理系统/a div classcollapse navbar-collapse ul classnavbar-nav me-auto li classnav-itema classnav-link href{% url book_list %}图书管理/a/li li classnav-itema classnav-link href{% url publisher_list %}出版社管理/a/li li classnav-itema classnav-link href{% url author_list %}作者管理/a/li /ul /div /div /nav div classcontainer mt-4 {% block content %}{% endblock %} /div /body /html列表页使用 Bootstrap 的表格组件并对关键词和出版社做了筛选表单!-- templates/books/book_list.html -- {% extends base.html %} {% block content %} div classd-flex justify-content-between align-items-center mb-3 h2图书列表/h2 a href{% url book_add %} classbtn btn-primary新增图书/a /div form methodget classrow g-3 mb-3 div classcol-md-4 input typetext namekeyword value{{ keyword }} classform-control placeholder搜索书名或ISBN /div div classcol-md-3 select namepublisher classform-select option value全部出版社/option {% for publisher in publishers %} option value{{ publisher.id }} {% if publisher_id publisher.id|stringformat:s %}selected{% endif %} {{ publisher.name }} /option {% endfor %} /select /div div classcol-md-2 button typesubmit classbtn btn-outline-secondary筛选/button /div /form table classtable table-striped table-hover thead tr th书名/ththISBN/thth出版社/thth作者/thth定价/thth库存/thth操作/th /tr /thead tbody {% for book in page_obj %} tr td{{ book.title }}/td td{{ book.ISBN }}/td td{{ book.publisher.name }}/td td {% for author in book.authors.all %} span classbadge bg-info text-dark{{ author.name }}/span {% endfor %} /td td{{ book.price }}/td td{{ book.stock }}/td td a href{% url book_edit book.pk %} classbtn btn-sm btn-warning编辑/a form methodpost action{% url book_delete book.pk %} classd-inline {% csrf_token %} button typesubmit classbtn btn-sm btn-danger onclickreturn confirm(确定删除吗)删除/button /form /td /tr {% empty %} trtd colspan7 classtext-center暂无图书数据/td/tr {% endfor %} /tbody /table !-- 分页 -- nav ul classpagination {% if page_obj.has_previous %} li classpage-itema classpage-link href?page{{ page_obj.previous_page_number }}上一页/a/li {% endif %} li classpage-item disabledspan classpage-link第 {{ page_obj.number }} / {{ page_obj.paginator.num_pages }} 页/span/li {% if page_obj.has_next %} li classpage-itema classpage-link href?page{{ page_obj.next_page_number }}下一页/a/li {% endif %} /ul /nav {% endblock %}分页部分我用 Bootstrap 的pagination组件直接渲染关键词和出版社筛选参数需要手动拼接放到分页链接里目前这个版本会有个小问题翻页时会丢失筛选条件。改进方式是在分页链接中把keyword和publisher_id加进 URLa classpage-link href?page{{ page_obj.next_page_number }}keyword{{ keyword }}publisher{{ publisher_id }}下一页/a4.5 扩展用 Django Admin 做数据辅助管理Django Admin 是一个隐藏的“效率外挂”。即便我写好了全套自定义页面Admin 后台仍然值得保留尤其适合数据导入和快速查改# books/admin.py from django.contrib import admin from .models import Publisher, Author, Book admin.register(Book) class BookAdmin(admin.ModelAdmin): list_display (title, ISBN, publisher, price, stock, publication_date) list_filter (publisher, publication_date) search_fields (title, ISBN) filter_horizontal (authors,)filter_horizontal让多对多关系的编辑界面变成双栏选择框比默认的下拉多选框好用太多。配置完 Admin日常录入少量测试数据时根本不需要写 SQL 或者造 HTML 表单。5. 常见问题与排查技巧实录5.1 MySQL 连接类问题现象原因解决方案Cant connect to MySQL serverMySQL 服务未启动或端口不对systemctl status mysql确认端口是 3306Table book_system.auth_user doesnt exist没有执行初始迁移python manage.py migrateUnknown character set: utf8mb4MySQL 版本过老升级 MySQL 到 5.7 以上django.db.utils.OperationalError: (2059)认证插件不兼容按上文改成mysql_native_password5.2 Django ORM 查询经典错误查询结果没有按预期排序Django 模型 Meta 中如果定义了ordering它会覆盖视图里的order_by吗不会视图中的order_by优先级更高。但要注意order_by要写在filter之后、切片之前顺序不对会报错。多对多关联在模板中访问报错如果在模板中写{{ book.authors.all }}需要在视图里确认authors有没有被正确预取。如果没加prefetch_related也不会报错只是多出 N 条查询数据量大了页面会明显变慢。删除对象后外键报错在外键字段指定on_deletemodels.CASCADE之前删除关联对象时会触发ProtectedError或RestrictedError。编辑模型后记得重新迁移。5.3 Bootstrap 静态资源加载不出来的排查使用 CDN 引入 Bootstrap 时页面无样式通常不是代码问题而是网络问题。CDN 被拦截或者访问缓慢时页面会裸奔。我的做法是开发环境用本地静态文件兜底部署到内网服务器时再决定是否切换 CDN 地址# settings.py STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static]本地下载bootstrap.min.css和bootstrap.bundle.min.js放到static/目录后模板中用{% load static %}引用link href{% static css/bootstrap.min.css %} relstylesheet5.4 执行删除对象时的意外数据丢失这是我最想提醒的一个坑。Django 默认的CASCADE删除会把你意料之外的关联数据一并删掉。假设我有一个Order模型外键指向了Book当删除图书时所有关联订单也会被删除。这在真实业务里非常危险。安全做法是给关键外键加上on_deletemodels.PROTECT这样删除受保护数据时会直接抛异常提醒你手动处理关联数据。如果你需要“软删除”可以给模型加一个is_active布尔字段默认True查询时过滤is_activeTrue。这比真正执行 DELETE 语句安全得多数据可追溯、可恢复。6. 项目优化与后续扩展建议6.1 查询性能从入门到进阶当图书数量破万列表页每次查询都全量取回数据响应时间会显著增加。除了select_related和prefetch_related我建议再做三件事为常查询字段添加数据库索引class Book(models.Model): ISBN models.CharField(max_length13, uniqueTrue, db_indexTrue) title models.CharField(max_length200, db_indexTrue)db_indexTrue会让 Django 在数据库层为 title 字段创建索引模糊搜索icontains也能借助索引但要注意前置通配符%keyword%是无法利用索引的。只取需要的字段列表页如果不需要summary这种大字段可以用.only(title, ISBN, price)或.values()限制返回字段减少网络传输和内存占用。合理使用 Django Debug Toolbar这个工具能直观展示每个页面执行了多少条 SQL、耗时多少。是排查 N1 问题的利器。6.2 从图书管理到通用 CRUD 平台的扩展思路这套架构做完之后你会发现它天然具备扩展成更通用后台系统的潜力。比如增加图书借阅功能新增BorrowRecord模型关联Book和User记录借出和归还日期增加库存预警当stock低于阈值时在列表页用 Bootstrap 警示条提示补货导出 Excel 报表使用openpyxl把查询结果导出为表格方便出版社对账。每一个扩展点的实现逻辑都和现有代码一致建模型 - 迁移 - 写视图 - 做模板。底层框架的功能边界很清晰剩下的就是业务想象力。最后补充一个部署时容易踩的坑很多人在本地跑得飞起一到服务器上就各种 404、静态文件找不到。如果你准备把项目部署到 Linux 服务器比如使用宝塔面板我的经验是先用 Nginx 做反向代理静态文件统一交给 Nginx 处理Django 只负责动态请求。但如果你暂时不想折腾 Nginx直接用python manage.py runserver 0.0.0.0:8000做内部测试也可以只是性能不太行。部署时最重要的一步是关闭调试模式DEBUG False ALLOWED_HOSTS [你的域名或IP]然后执行python manage.py collectstatic把所有静态文件收集到STATIC_ROOT目录Nginx 直接指向这个目录就能正常加载 Bootstrap 样式。这个顺序我当年反着折腾了半天页面始终裸奔后来才发现是静态文件收集的问题。图书管理系统本身不复杂但把环境、模型、视图、模板、部署这一整套链路走通你会对整个 Django 项目的生命周期有非常完整的认识。本文还有配套的精品资源点击获取
返回列表