ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Django的读书节宣传系统:毕业设计实战与避坑指南

基于Django的读书节宣传系统:毕业设计实战与避坑指南 1. 项目概述与核心需求拆解1.1 这个毕业设计到底在做什么说到计算机专业的毕业设计每年都有一大批同学在“选题—拖延—赶工—答辩”的循环里挣扎。而我又一次接到的这个“江城读书节宣传系统的设计与实现”乍看只是常见的CRUD管理系统但真上手做下来才发现它把Web开发里的很多经典知识点都串起来了非常适合作为毕业设计。简单说这个系统要解决的问题很明确读书节这类文化活动需要对外展示活动信息、推荐书目、发布动态还要能管理报名用户和活动数据光靠公众号推文和Excel表格根本撑不住。如果你正在选毕设题目又在纠结是选“图书管理系统”“校园活动报名系统”还是“宣传网站”那我建议你把目光放在这个“读书节宣传系统”上。它比单纯的图书管理多了活动运营属性比纯展示型官网多了后台管理和用户交互难度适中演示效果好答辩时也容易讲出东西。1.2 适合谁参考以及系统能带来什么价值这套系统适合三类人来参考第一类是计算机专业大四学生需要完成毕业设计并顺利通过答辩第二类是想转行做后台开发的人想通过一个完整的项目把Django的知识串起来第三类是想给社团、文化机构做活动网站的非技术人员至少能通过这篇文了解一套活动宣传系统的构成和成本。技术栈方面核心是Python的Django框架。为什么选它后文我会详细展开但一句话概括就是Django自带Admin后台、ORM数据库操作、用户认证体系意味着你能在两周内做出一个带管理端和用户端的完整系统这在毕设周期里太重要了。你别指望像大厂一样用Spring Cloud微服务去拆一个读书节宣传系统——那就是拿大炮打蚊子自己累死不说答辩老师还会追问你“为什么这么复杂”。2. 功能模块设计与技术选型思路2.1 模块划分从用户和运营两个视角倒推在动手写代码之前我习惯先画功能脑图。这个系统站在普通访客的角度需要看到读书节的活动介绍、日程安排、书单推荐、往届精彩瞬间还要能在线报名参加活动、收藏感兴趣的书目。站在后台管理员的角度需要管理轮播图、发布活动公告、审核报名、维护书单数据、查看报名统计。所以我把系统拆成几个核心模块模块面向对象核心功能活动宣传模块游客展示活动首页、活动详情、日程表图书推荐模块游客/注册用户书单列表、图书详情、收藏功能在线报名模块注册用户活动报名、报名状态查询、取消报名后台管理模块管理员内容发布、报名审核、数据统计用户系统全部注册、登录、个人信息维护这个拆分方式的好处是每个模块都可以单独讲解答辩时老师问“你的系统有哪些功能”你能清楚说出五个模块并且每个模块都能演示而不是笼统地扯“系统管理”。2.2 为什么选Django而不是Java、PHP或小程序题目里提到了django、java、PHP、小程序这些热词我自己也带过不少做毕设的同学这里说点掏心窝的话选技术栈不是选最火的而是选最适合你当前目标和时间预算的。如果你选Java意味着你要面对Spring Boot MyBatis Plus Maven依赖管理这一整套生态光环境配置和依赖冲突就能耗掉一周选PHP的话原生语法写起来快但前后端分离、接口规范这些现代开发方式你在毕业设计里反而不容易体现“设计感”小程序则是另一种思路——微信小程序做前端后端还是要有人做你还得解决审核、真机调试、域名备案这些额外事务。而Django有这些让毕设党的日子好过很多的特质自带Admin后台。模型建好后Django自动生成管理界面你不用额外写后台页面的代码。对于那些“只要增删改查”的管理操作等于白送。ORM写起来像写Python。查询图书、筛选热门书目这类操作不用拼接SQL代码可读性高答辩时说“数据层完全面向对象”显得很专业。用户认证模块完整。注册、登录、密码加密、会话管理一套现成的。你在Java里要搬Spring Security配置写半天在Django里几行代码就接上了。模板语法简单。我后面会展示一个循环渲染活动列表三行模板代码搞定前端基础一般的同学也能快速出页面。2.3 数据库怎么做表设计这一部分我让你直接照着敲都没问题。数据库我用的是MySQL因为毕设答辩时老师大概率会问一句“你用的什么数据库为什么不用SQLite”——你说MySQL是生产环境常见的选择显得你有考虑过部署场景这就够了。核心表设计思路如下活动表Activity存读书节各类活动的标题、简介、开始时间、结束时间、地点、封面图、最大报名人数。图书表Book存推荐书目的书名、作者、出版社、ISBN、简介、封面、所属分类。用户表User使用Django内置的auth.User扩展增加手机号、学号/工号、头像等字段。报名表SignUp关联用户ID和活动ID记录报名时间、状态待审核/已通过/已取消。收藏表Favorite关联用户ID和图书ID记录收藏时间。这里要特别注意一个细节点报名表为什么要单独建因为一个用户可能报名多个活动一个活动也有多个用户报名这是典型的多对多关系但报名状态是“按次记录”的所以不能简单用ManyToManyField而是建一张中间表手动管理。这个细节拿去答辩讲比背概念强一百倍。3. 核心功能实现与关键代码解析3.1 项目创建与基础配置实操老规矩先创建虚拟环境这一步能避免你后面把依赖装得乱七八糟而且写进毕设文档里是“规范开发”的加分项。直接在终端里依次执行mkvirtualenv reading_festival pip install django4.2.5 mysqlclient pillow django-admin startproject festival_project cd festival_project python manage.py startapp festival这里装pillow是因为要处理图片上传不装的话管理员在后台传活动封面立刻给你报错。装mysqlclient是因为Python连MySQL需要这个驱动。然后打开settings.py把festival应用注册进去顺便配置数据库连接。Django默认连的是SQLite你要切换成MySQL就在DATABASES字段里这样改DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: reading_festival, USER: root, PASSWORD: 你自己设的密码, HOST: 127.0.0.1, PORT: 3306, } }同时在文件里补上静态资源和媒体文件的配置以后上传的封面图会保存在项目的media目录下import os MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media) STATIC_URL /static/ STATICFILES_DIRS [os.path.join(BASE_DIR, static)]3.2 模型编写把表结构转成代码现在开始建模型。这一步是核心中的核心模型写得好后台管理和前端展示都顺畅。打开festival/models.py按下面写from django.db import models from django.contrib.auth.models import User class Activity(models.Model): STATUS_CHOICES ( (draft, 未开始), (open, 报名中), (closed, 已截止), (done, 已结束), ) title models.CharField(活动名称, max_length100) description models.TextField(活动简介) start_time models.DateTimeField(开始时间) end_time models.DateTimeField(结束时间) location models.CharField(活动地点, max_length200) cover models.ImageField(封面图, upload_toactivity_covers/, blankTrue) max_people models.IntegerField(名额上限, default100) status models.CharField(状态, max_length10, choicesSTATUS_CHOICES, defaultdraft) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.title class Book(models.Model): title models.CharField(书名, max_length200) author models.CharField(作者, max_length100) publisher models.CharField(出版社, max_length100) isbn models.CharField(ISBN, max_length20) summary models.TextField(内容简介) cover models.ImageField(封面图, upload_tobook_covers/, blankTrue) category models.CharField(分类, max_length50, blankTrue) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.title class SignUp(models.Model): STATUS_CHOICES ( (pending, 待审核), (approved, 已通过), (cancelled, 已取消), ) user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name报名用户) activity models.ForeignKey(Activity, on_deletemodels.CASCADE, verbose_name报名活动) status models.CharField(状态, max_length10, choicesSTATUS_CHOICES, defaultpending) sign_time models.DateTimeField(报名时间, auto_now_addTrue) class Meta: unique_together (user, activity) verbose_name 活动报名 verbose_name_plural 活动报名 def __str__(self): return f{self.user.username} - {self.activity.title} class Favorite(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name用户) book models.ForeignKey(Book, on_deletemodels.CASCADE, verbose_name图书) created_at models.DateTimeField(收藏时间, auto_now_addTrue) class Meta: unique_together (user, book) verbose_name 图书收藏 verbose_name_plural 图书收藏 def __str__(self): return f{self.user.username} - {self.book.title}写完后执行两条命令同步数据库python manage.py makemigrations python manage.py migrate3.3 视图与路由把每个页面串起来模型建好只是地基接下来要看Django怎么把数据交给页面。先写视图核心是活动列表页。在festival/views.py里from django.shortcuts import render, get_object_or_404 from .models import Activity, Book def activity_list(request): activities Activity.objects.filter(status__in[open, draft]).order_by(start_time) return render(request, festival/activity_list.html, {activities: activities}) def activity_detail(request, pk): activity get_object_or_404(Activity, pkpk) signed_count activity.signup_set.count() ctx { activity: activity, signed_count: signed_count, } return render(request, festival/activity_detail.html, ctx) def book_list(request): books Book.objects.all().order_by(-created_at) return render(request, festival/book_list.html, {books: books})然后配置路由。在festival/urls.py里写from django.urls import path from . import views urlpatterns [ path(activities/, views.activity_list, nameactivity_list), path(activities/int:pk/, views.activity_detail, nameactivity_detail), path(books/, views.book_list, namebook_list), ]再去主项目的urls.py注册一下from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static urlpatterns [ path(admin/, admin.site.urls), path(, include(festival.urls)), ] if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)3.4 页面模板三行模板语法渲染一书架书Django的模板系统是它最亲民的地方你不用懂JS框架也能做动态页面。我用图书列表页举例在templates/festival/book_list.html里{% extends base.html %} {% block content %} div classcontainer h2 classsection-title推荐书单/h2 div classbook-grid {% for book in books %} div classbook-card img src{{ book.cover.url }} alt{{ book.title }} h3{{ book.title }}/h3 p{{ book.author }} · {{ book.publisher }}/p p classbook-summary{{ book.summary|truncatechars:60 }}/p a href{% url book_detail book.pk %} classbtn查看详情/a /div {% empty %} p暂无推荐书目敬请期待。/p {% endfor %} /div /div {% endblock %}{% for %}循环、{{ book.title }}变量插值、{% empty %}空列表提示、truncatechars过滤器这几样搞明白Django模板你已经会用八成。{{ book.cover.url }}这句会返回图片的实际访问URL前提是MEDIA配置正确。4. 报名流程设计用户注册、登录与状态管理4.1 用Django内置认证搞定用户体系用户系统我直接用的Django内置auth应用因为自己写密码哈希和会话管理纯属给自己挖坑。你要做的核心工作只有两件一是实现注册、登录、登出的视图函数二是在报名时判断用户是否已登录。注册视图我给了参考from django.shortcuts import render, redirect from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import login def register(request): if request.method POST: form UserCreationForm(request.POST) if form.is_valid(): user form.save() login(request, user) return redirect(activity_list) else: form UserCreationForm() return render(request, registration/register.html, {form: form})这里要强调一个毕设答辩必问的点UserCreationForm默认不带邮箱、手机号这些字段所以你如果想让用户填手机号需要自己写一个继承表单类。展示代码如下from django import forms from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User class RegisterForm(UserCreationForm): email forms.EmailField(requiredTrue) mobile forms.CharField(max_length11, requiredTrue) class Meta: model User fields (username, email, mobile)4.2 报名功能的完整逻辑真正的核心是报名逻辑不能只做一个“点了按钮就插入一条记录”的假功能。你必须考虑三个问题这个活动是否还在报名期、用户是否已经报过名、名额是否已满。参考实现如下from django.shortcuts import get_object_or_404, redirect from django.contrib import messages from django.contrib.auth.decorators import login_required from django.utils import timezone from .models import Activity, SignUp login_required def activity_signup(request, pk): activity get_object_or_404(Activity, pkpk) # 判断活动是否在报名期 if activity.status ! open or activity.end_time timezone.now(): messages.error(request, 该活动不在报名期内) return redirect(activity_detail, pkactivity.pk) # 判断用户是否重复报名 if SignUp.objects.filter(userrequest.user, activityactivity).exists(): messages.warning(request, 您已经报过名了) return redirect(activity_detail, pkactivity.pk) # 判断名额是否已满 if activity.signup_set.filter(statusapproved).count() activity.max_people: messages.error(request, 活动名额已满) return redirect(activity_detail, pkactivity.pk) signup SignUp.objects.create( userrequest.user, activityactivity, statuspending ) messages.success(request, 报名成功请等待管理员审核) return redirect(activity_detail, pkactivity.pk)这里有个我没明说但你该注意到的小细节报名记录建的时候状态是pending不是直接approved。为什么因为“管理员审核”这个动作一旦参与进来整个系统就从“学生项目”变成了“有业务流程的系统”答辩时高下立判。你可以在后台Admin里审核通过也可以在用户视图里开发“我的报名-列表-状态”页面。反正逻辑上你留了审核这个口子就不会被问住。5. 后台管理配置少写一百行代码的秘诀5.1 注册Admin模型后台直接管理数据Django的Admin是我决定薅的最大羊毛。在festival/admin.py里注册模型后所有数据都能在后台管理界面操作。代码少到令人发指from django.contrib import admin from .models import Activity, Book, SignUp, Favorite admin.register(Activity) class ActivityAdmin(admin.ModelAdmin): list_display (title, start_time, end_time, location, status) list_filter (status, start_time) search_fields (title, location) date_hierarchy start_time admin.register(Book) class BookAdmin(admin.ModelAdmin): list_display (title, author, publisher, category) search_fields (title, author) list_filter (category,) admin.register(SignUp) class SignUpAdmin(admin.ModelAdmin): list_display (user, activity, status, sign_time) list_filter (status, activity) search_fields (user__username, activity__title)search_fields里写user__username意思是跨表查询用户表的username字段。这个下划线双写是Django ORM的规则后台搜索框立马就能按用户名搜报名记录了非常方便。这也算是我实操里用的最频繁的一个技巧。5.2 用list_filter和date_hierarchy让筛选一目了然很多人刚接触Admin只看得到默认表格不知道右边的筛选器可以自己配。加了list_filter (status, start_time)之后后台就会自动出现“按状态筛选”的下拉栏。加了date_hierarchy start_time之后顶部还会多一个按日期钻取的年/月/日导航条。这两个配置能让你的毕业设计演示时显得很“会”老师问“你怎么管理活动状态”你直接打开Admin页面点一下筛选当场给他看效果。零成本装杯划算。6. 数据展示增强让首页真正有“宣传感”6.1 首页聚合多个模块的数据如果你一上来就甩给用户一个平平无奇的列表页那是浪费了“宣传系统”这个定位。宣传系统的首页应该像一张活动海报把最能吸引人的内容聚合在一起头图轮播放最热活动中间是精选书单下方是近期日程表。实现方法是写一个home视图把多个查询结果打包返回from django.shortcuts import render from .models import Activity, Book def home(request): latest_activities Activity.objects.filter(status__in[open, draft]).order_by(start_time)[:3] featured_books Book.objects.order_by(-isbn)[:4] upcoming_activities Activity.objects.filter(start_time__gtetimezone.now()).order_by(start_time)[:5] ctx { latest_activities: latest_activities, featured_books: featured_books, upcoming_activities: upcoming_activities, } return render(request, festival/home.html, ctx)然后在home.html模板里用几个for循环把数据渲染成三个板块。这样页面信息量立刻丰富起来老师看到首页时会觉得“这小子做了不少工作”其实每个板块都是同一套模板语法工作量完全可控。6.2 给页面加点动态效果轮播图与懒加载纯后台渲染只能出静态页面你再想让界面好看一点不需要上React用原生JS或一点jQuery就够了。我推荐做一个自动轮播的首页Banner后台在模型里加一个is_banner布尔字段标记哪些活动放轮播然后在模板里配合CSS动画实现渐变切换。另外图书列表页图片多一页下来加载压力不小。用浏览器原生的loadinglazy属性就能解决img src{{ book.cover.url }} alt{{ book.title }} loadinglazy这个属性一行代码实现图片懒加载不用引任何库。7. 常见问题排查与避坑指南7.1 学习路线避坑指南题目的相关热词里全是坑啊。这里提两个最常见的django执行查询-删除对象新手常写Book.objects.get(pk1).delete()然后忘了赋值或没有try-except结果对象不存在时就抛DoesNotExist异常导致整个页面崩掉。正确做法是先判断存在book Book.objects.filter(pk1).first() if book: book.delete()django创建app创建完app忘记在INSTALLED_APPS注册结果一运行就报No module named festival。注意注册的是配置类名festival.apps.FestivalConfig或直接用festival即可。7.2 图片上传失败的连环坑图片报错算是我被问得最多的问题刚写完模型一传图就报错特别容易劝退新人。先看你的控制台报错分两种一是没装pillow会直接提示ModuleNotFoundError: No module named PIL。这个好办pip install pillow解决。二是admin里看得到图片但前端页面图片裂了。这是MEDIA配置没生效。检查你有没有在根urls.py里加static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)同时确认图片真的上传到了media目录下面。7.3 报名功能开发中容易踩的坑我先把自己踩过的记住报名表里用了unique_together (user, activity)做唯一约束这本身没毛病但如果你在判断“是不是重复报名”时先查一次库在创建时又没加get_or_create或异常处理并发情况下可能重复写入报错。毕设里并发量小一般不会有问题但你在文档里提一嘴“这里用唯一约束保证数据一致性”比不讲原因强很多。还有一个坑是on_deletemodels.CASCADE。我的建议是把报名记录关联到用户时用CASCADE没问题但你把图书推荐表和用户收藏表关联时删除图书会把所有用户的收藏记录也删了这在真实业务里比较危险。毕设答辩时你可以主动说一句“真实生产环境我会把用户产生的数据用PROTECT保护起来”这句话会让人觉得你考虑到了数据安全能加分。7.4 让查询变快的两个小技巧如果你的图书表里存了上千条数据列表页开始变卡可以试试两个优化法一是给经常查询的字段加索引title models.CharField(书名, max_length200, db_indexTrue) isbn models.CharField(ISBN, max_length20, db_indexTrue)二是使用select_related或prefetch_related解决N1查询问题。比如首页展示活动列表时每个活动都要查一次关联的报名人数普通写法会产生N1条SQL。用annotate一次查出from django.db.models import Count Activity.objects.annotate(signup_countCount(signup))这样一条SQL就搞定所有统计。答辩的时候能说出这个词老师就知道你真的懂性能优化。8. 个人经验与扩展方向8.1 从毕设到项目的思考做完这套系统有两点我自己体会最深。第一点毕设不是越复杂越好而是越“完整”越好。一个从前端页面、后台CRUD、用户交互到数据库设计、部署上线每个环节都能说清楚的项目比一个听起来是分布式高并发的“纸老虎”拿分高得多。第二点带着运营视角做技术页面和功能才能立住。你如果只是机械地增删改查做完之后你可能连自己系统有哪些页面都讲不清但如果先想“用户来了怎么看活动、怎么报名、管理员怎么处理”系统的脉络就会自然浮现。8.2 后续还能怎么扩展如果你时间充裕想在本项目基础上做增量我有三个建议方向一是在现有系统上加一个数据可视化统计页面展示每个活动的报名人数趋势、图书分类占比用ECharts实现。视觉冲击力强工作量可控答辩时讲“用图表驱动运营决策”立刻拔高格局。二是把前端升级成小程序版本。我见过很多同学这样组合毕业设计Django做后端API小程序做前端展示端一套代码管两个端。当然前提是你有时间学小程序基础否则风险不小。三是引入Redis做活动热门度排行首页显示“最热活动Top5”。这个方向能展示你对缓存的理解但需要额外搭Redis环境对于毕设来说性价比略低看你自己取舍。最后说一句真心话拿到项目之后先梳理需求再搭环境再写代码每一个流程都要留下截图和记录。这些截图后期放进展会设计文档里排版漂亮答辩时老师问“你的开发过程是怎样的”你直接把文档翻给他看那种从容比你临时编故事要强得多了。
返回列表