
简介这是一套基于Python Django框架的图书管理系统完整源码面向Web开发初学者与课程设计人员覆盖出版社、作者、图书三类核心数据的增删改查及借阅管理。资源共63个文件包括13个Python源码、11个HTML模板以及Bootstrap样式、JavaScript脚本、功能截图与字体资源整体压缩包约1.33MB目录结构清晰便于本地部署与二次开发。系统采用MVC分层设计内置用户登录、权限控制、图书检索、借阅归还等功能MySQL数据库配合Django ORM简化数据操作Bootstrap保证界面响应式展示。已有322人学习参考适合作为毕业设计或课程项目的直接基础。通过分析项目配置、models模型、views视图与templates模板可快速掌握Django全流程开发思路并直接运行或改造出功能更完善的管理系统。 图书管理系统这个题目说真的从我当年做课程设计起就一直火到现在每年都有人用 Python Django bootstrap MySQL 这套组合把它重新做一遍。我也帮人 debug 过不少次这类项目踩坑点来来去去就那么几个但很多人第一次做的时候依然会在同样的地方卡住。这个项目本质上是典型的 MVC 三层结构核心业务就三块出版社管理、作者管理、图书管理外加它们之间的关联关系。它既能当练手项目打通 Web 开发全流程也能直接扩展成毕业设计甚至小型企业的图书管理后台。如果你正处于 Django 入门到进阶的过渡阶段或者正在准备课程设计、简历项目这篇文章可以帮你把整套技术栈串起来顺便避开我踩过的那些坑。1. 项目整体思路与数据模型设计1.1 实体关系梳理为什么是这三张表图书管理系统的核心在于实体建模。出版社、作者、图书这三个对象看起来都有各自的增删改查但真正有点意思的是它们之间的关系。出版社和图书是一对多关系一个出版社可以出版很多本书一本书只属于一个出版社。这在数据库里用外键ForeignKey表达。作者和图书是多对多关系一本书可能有多个作者一个作者也可能写多本书。这在 Django 里直接用 ManyToManyField 处理Django 会自动帮你生成一张中间表不需要手动建第三张表。我见过不少初学者把这三张表做成完全独立、互不关联的“三个单表 CRUD”那样就失去了这个项目的灵魂。关系型数据库的核心就是关系面试官问起项目里外键、多对多、联表查询你连数据模型都没建对后面基本没法聊。1.2 数据模型代码与字段设计思路以我常用的模型设计为例三个模型分别放在 book 这个 app 的 models.py 里from django.db import models class Publisher(models.Model): name models.CharField(出版社名称, max_length100, uniqueTrue) address models.CharField(地址, max_length200, blankTrue) created_time models.DateTimeField(auto_now_addTrue) class Meta: verbose_name 出版社 verbose_name_plural verbose_name ordering [-created_time] def __str__(self): return self.name class Author(models.Model): name models.CharField(姓名, max_length50) email models.EmailField(邮箱, blankTrue) bio models.TextField(简介, blankTrue) created_time models.DateTimeField(auto_now_addTrue) class Meta: verbose_name 作者 verbose_name_plural verbose_name def __str__(self): return self.name class Book(models.Model): title models.CharField(书名, max_length200) publisher models.ForeignKey( Publisher, verbose_name出版社, on_deletemodels.CASCADE, related_namebooks ) authors models.ManyToManyField( Author, verbose_name作者, related_namebooks ) publish_date models.DateField(出版日期, nullTrue, blankTrue) price models.DecimalField(定价, max_digits6, decimal_places2, default0) isbn models.CharField(ISBN, max_length20, blankTrue) created_time models.DateTimeField(auto_now_addTrue) class Meta: ordering [-created_time] def __str__(self): return self.title这里说几个关键选择背后的原因外键 on_delete 参数CASCADE 表示出版社删除时该出版社下的图书也一起删除。如果希望保留图书但把出版社置空可以用 SET_NULL但前提是外键字段设置 nullTrue。课程设计阶段直接用 CASCADE 最省事但真实项目中要根据业务场景考虑是否用 PROTECT。related_name不写的话默认是 book_set写清楚让你在查询时能直接通过 publisher.books.all() 拿到这个出版社的所有书很顺手。price 用 DecimalField 而不是 FloatField钱这种东西用浮点数存会有精度问题DecimalField 在数据库层面就是定点数排序和计算更可靠。1.3 为什么要把出版社单独建表而不是存字符串这是我在指导新人时最常被问到的问题之一图书表里直接放一个 publisher_name 字段不行吗何必多一张表再搞外键行是行但代价很大。出版社名称如果直接冗余在图书表里哪天出版社改名了你得更新所有图书记录如果同一个出版社名称写的时候手抖了一个字统计时就变成两个出版社了。拆成独立的表之后出版社只有一份记录图书通过外键引用它的 id数据一致性由数据库约束保证。这就是数据库三大范式的思想在落地项目里的体现。做课程设计不一定非要背范式定义但你把这个理由讲清楚面试官基本就满意了。2. 环境准备与 Django 工程初始化2.1 版本选型不要盲目追新先讲版本选择。我建议的组合是 Python 3.10、Django 4.2 LTS、MySQL 8.0、Bootstrap 5。为什么选 Django 4.2 LTS 而不是最新的 5.xLTS 是长期支持版本官方维护时间长网上资料最多你遇到的大多数报错在 Stack Overflow 上都能搜到答案。Django 5.x 当然更先进但对一个练手项目来说稳定性和生态比新特性重要。MySQL 8.0 是目前绝大多数环境默认安装的版本注意它的默认认证插件是 caching_sha2_password连接时要用 PyMySQL 1.0 或者 mysqlclient老版本驱动可能会报错。如果你用的是 Python 3.10 以上的版本建议干脆用 PyMySQL兼容性最省心。Bootstrap 用 5.x 就行和 3/4 相比默认样式好看很多而且不需要 jQuery 依赖简单清爽。2.2 项目初始化的完整操作假设你已经装好了对应环境下面是完整操作流程。先用虚拟环境隔离依赖这一步别偷懒否则后面 pip 全局装一堆包会乱套# 创建项目目录并进入 mkdir book_system cd book_system # 创建并激活虚拟环境 python -m venv venv # Windows 下激活 # venv\Scripts\activate # macOS / Linux 下激活 # source venv/bin/activate # 安装依赖 pip install django pymysql # 创建 Django 项目和应用 django-admin startproject config . python manage.py startapp book说一下为什么把项目目录叫 configDjango 项目配置文件和 book 业务应用区分开后续部署到服务器、配置多个 app 时不会乱。创建完记得在 settings.py 的 INSTALLED_APPS 里注册 book 这个应用这一步很多人忘了后面 makemigrations 会提示找不到模型。2.3 连接 MySQL 的配置细节先在 MySQL 里建好数据库字符集一定要用 utf8mb4这是很多人踩坑的重灾区我后面专门讲CREATE DATABASE book_system 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, }, } }接下来在你创建的 appbook的init.py 里加入这段代码让 Django 用 PyMySQL 驱动import pymysql pymysql.install_as_MySQLdb()这一步的作用是让 PyMySQL 伪装成 MySQLdb 提供给 Django 使用。如果你不写这段直接跑 migrate 会报No module named MySQLdb。还有两个中文字符相关的设置强烈建议改掉LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai USE_TZ FalseUSE_TZ 这个很多人都劝你设成 True但对这个项目来说你会直接用 datetime.now() 存时间如果你不想在 timezone 上绕来绕去设成 False 会省很多事。等以后做国际化项目再深入研究时区也不迟。2.4 生成数据库表结构模型写好后执行迁移两步走python manage.py makemigrations python manage.py migratemakemigrations 会根据模型变化生成迁移文件migrate 才会把这些变更真正写入数据库。很多人跑 migrate 之前忘了 makemigrations结果提示 No changes detected。这两步是 Django 的典型工作流每次改完模型都要重跑一遍。3. 核心功能实操图书、作者、出版社的增删改查3.1 用 Django 后台快速打通数据入口说实话如果目标只是把数据跑通Django 自带的 admin 后台五分钟就够用。在 book/admin.py 里注册模型from django.contrib import admin from .models import Publisher, Author, Book admin.register(Publisher) class PublisherAdmin(admin.ModelAdmin): list_display [id, name, created_time] admin.register(Author) class AuthorAdmin(admin.ModelAdmin): list_display [id, name, email] admin.register(Book) class BookAdmin(admin.ModelAdmin): list_display [id, title, publisher, publish_date, price] list_filter [publisher, authors] search_fields [title, publisher__name, authors__name]然后用python manage.py createsuperuser创建管理员账号启动服务后访问http://127.0.0.1:8000/admin就能在网页上做数据录入了。注意这里 list_filter 和 search_fields 直接使用了外键跨表查询比如publisher__name这是 Django 的跨关系查询语法双下划线表示“取关联对象的某个字段”。在图书列表页里能直接按出版社名称过滤、按作者名称搜索体验立马上一个档次也是你写进简历时能说出口的细节。3.2 Bootstrap 前端页面与模板继承后台只是给管理员用的真正的项目还得有个前台页面。Bootstrap 推荐直接用 CDN 引入避免本地静态文件配置的麻烦link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/css/bootstrap.min.css relstylesheet script srchttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/js/bootstrap.bundle.min.js/script为了不让每个页面重复写整段 HTML用模板继承建一个 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-dark div classcontainer a classnavbar-brand href/图书管理系统/a div classcollapse navbar-collapse ul classnavbar-nav 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 /htmlBootstrap 的核心玩法就是 class 命名完了效果直接出来不需要写 CSS。比如表格加个table table-striped table-hover卡片用card按钮用btn btn-primary分页用pagination这些基础组件足够把页面做得像模像样。3.3 图书列表页与新增图书功能的完整实现我以图书列表和新增功能为例把视图、URL、模板串起来跑一遍。先写视图。用 Django 的类视图 ListView 可以减少样板代码但为了让新手看得更直观也可以先用函数视图。我建议两种都试试这里展示更贴近实际开发习惯的类视图写法from django.views.generic import ListView, CreateView from django.urls import reverse_lazy from .models import Book from .forms import BookForm class BookListView(ListView): model Book template_name book/book_list.html context_object_name books paginate_by 10 class BookCreateView(CreateView): model Book form_class BookForm template_name book/book_form.html success_url reverse_lazy(book_list)对应的 url 配置from django.urls import path from . import views urlpatterns [ path(books/, views.BookListView.as_view(), namebook_list), path(books/add/, views.BookCreateView.as_view(), namebook_add), ]再写一个 forms.py用 ModelForm 自动生成表单字段from django import forms from .models import Book class BookForm(forms.ModelForm): class Meta: model Book fields [title, publisher, authors, publish_date, price, isbn] widgets { publish_date: forms.DateInput(attrs{type: date}), }表单模板 book_form.html{% extends base.html %} {% block content %} div classcard div classcard-header新增图书/div div classcard-body form methodpost {% csrf_token %} {{ form.as_p }} button typesubmit classbtn btn-primary保存/button a href{% url book_list %} classbtn btn-secondary取消/a /form /div /div {% endblock %}这里最容易被忽略的是{% csrf_token %}。Django 默认开启了 CSRF 防护表单 POST 提交不带 token 的话直接返回 403。我见过很多新手折腾半天就是没加这一行。3.4 分页功能的三行代码图书数量一多列表页就要分页。Django 内置 Paginator 非常方便上面 ListView 里只需要写一行paginate_by 10模板里再加个分页组件{% if is_paginated %} 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 {% endif %}注意这里的模板变量是 ListView 自动注入的 page_obj不需要手动做任何处理。4. 数据库查询优化与常见坑点排查4.1 列表页 N1 查询问题的解决图书列表页上如果要展示出版社和作者名字最容易出现 N1 查询问题。什么是 N1就是查询所有图书只用了 1 条 SQL但循环每本书取出版社名称时又各发了一条 SQL总共 1N 条。尤其多对多关系的作者字段一本多个作者循环取作者列表会产生大量额外查询。解决办法是在查询集上提前把关联数据取出来class BookListView(ListView): def get_queryset(self): return (Book.objects .select_related(publisher) .prefetch_related(authors))select_related 用于外键关系它通过 JOIN 一次把出版社查出来prefetch_related 用于多对多关系它另外发一条查询把中间表和作者表的数据一并取回。这两行代码在数据量稍微上来之后性能差别非常明显是 Django 面试里几乎必问的优化点。4.2 数据库连接报错速查表我汇总一下实际项目中遇到最多的数据库相关报错和解决办法这部分内容网上虽然都有但整理成一个表方便你排查报错信息原因解决办法No module named MySQLdb没有让 PyMySQL 伪装成 MySQLdb在 app 的init.py 中执行 pymysql.install_as_MySQLdb()django.db.utils.OperationalError: (1045, Access denied...)用户名或密码错误或权限不足检查 settings.py 中 USER/PASSWORD用 MySQL 客户端手工登录验证django.db.utils.OperationalError: (2003, Cant connect...)MySQL 服务没启动或端口/HOST 配置不对确认 MySQL 服务已启动检查 3306 端口是否被占用(1366, Incorrect string value...)数据库字符集不是 utf8mb4建库时指定 utf8mb4或者 ALTER DATABASE 修改字符集django.db.utils.ProgrammingError: (1146, Table doesnt exist)忘记执行 migrate检查是否执行了 python manage.py migrateUnknown column xxx_id in field list模型改了字段表结构没同步重新 makemigrations 和 migrate4.3 删除对象时需要注意的联动逻辑Django 的查询集QuerySet删除有两种方式obj.delete()和QuerySet.delete()。单条记录调用 delete() 时Django 会去检查外键关联比如你删除一个有图书的出版社如果 on_deletemodels.CASCADE所有关联图书也会被删除如果想保留图书就得改成 SET_NULL 并在字段上允许 null。批量删除时还有个小坑Book.objects.filter(publisher_id1).delete()会返回删除的记录数和每个对象类型删除的具体数量。如果你设置了下级关联Django 会递归删除所有关联所以批量操作前建议先用.count()确认要删多少条再动手删。这条经验我在实际项目里救过我很多次。4.4 静态文件与 Bootstrap 样式的两种落地方式用 CDN 的时候静态文件的一切烦恼都不存在问题是部署到内网环境时没有外网样式全丢。本地化 Bootstrap 的做法是把 bootstrap.min.css 和 bootstrap.bundle.min.js 放到 book/static/ 目录下然后STATIC_URL /static/模板里改用{% load static %} link href{% static bootstrap/css/bootstrap.min.css %} relstylesheet注意开发环境下 Django 会自动帮你处理静态文件但部署到生产环境时这个功能默认关闭需要通过 TERMINAL 工具或者专门的静态文件服务来处理这是后话。做课程设计时用 CDN 就够了但要理解两种方式的区别。4.5 表单提交常见的 403/405 问题403 大部分是因为少了{% csrf_token %}前面已经提过而 405 Method Not Allowed 则通常是视图只支持 GET 不支持 POST或者 URL 里把 GET/POST 该用的视图写混了。Django 的 CreateView 本身同时支持 GET 和 POST但如果你自己写了函数视图只写了 GET 分支POST 过来就会 405。排查的时候先看浏览器开发者工具的 Network 面板确认请求方法是 GET 还是 POST再对照视图函数里有没有处理对应方法。另外如果表单页面能打开但提交后 URL 还是原来的没有跳转到 success_url多半是表单校验没通过。这时在模板里加一行{{ form.errors }}就能看具体报错比盲猜高效得多。最后再分享一个调试技巧我每次做这类 Django 项目都会先装一个 django-debug-toolbar它能显示当前页面执行了多少条 SQL、耗时多少查 N1 问题一目了然。数据量小的时候看不出问题等你塞进去几百本测试数据性能差异立刻暴露。这个项目跑通之后建议你接着把编辑、删除功能补齐再把前台展示做精致一点整套走下来Django 的开发流程基本就给你摸透了。本文还有配套的精品资源点击获取