ARTICLE DETAIL

资讯详情

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

Django旅游论坛毕设项目:Python Web开发实战指南

Django旅游论坛毕设项目:Python Web开发实战指南 简介本资源是一套完整的基于Django框架开发的旅游攻略论坛交流系统毕业设计源码面向计算机专业本科生及Python Web初学者解决旅游爱好者线上分享攻略、互动交流与内容管理的实际需求。系统涵盖用户注册登录、图文攻略发布、多版块社区论坛综合交流/旅游心得/杂谈、关注机制、个人主页含帖子/评论/资料管理、邮箱与手机双重绑定以及管理员后台全量CRUD功能技术栈明确适配PyCharm Django 3.0 Python 3.7 MySQL 5.6。压缩包共341个文件含90个核心Python后端逻辑文件、79个JavaScript交互脚本、45张JPG攻略配图、37个CSS样式文件及23个SVG图标资源结构完整、前后端分离清晰总大小12.84MB。目前已有220人学习下载提供可直接运行的完整工程、BootstrapFontAwesome前端组件集成、响应式页面及MySQL初始化SQL脚本开箱即用是理解Django MTV架构与社区类Web系统开发的优质实践案例。1. 为什么一个“旅游攻略论坛”能成为 Django 初学者最稳的毕业项目突破口不是所有毕设都值得花三个月反复调试数据库迁移、权限校验和富文本渲染——但这个 Python Django MySQL 的旅游攻略论坛系统恰恰卡在「够真实、够完整、够可控」的黄金交点上。它不碰高并发秒杀不卷实时音视频却完整覆盖用户注册登录、景点发帖、图文混排、评论点赞、后台管理、权限分级游客/普通用户/版主/管理员等 8 类核心 Web 业务模块PyCharm 调试友好、Django ORM 自带事务与迁移工具、MySQL 本地部署零成本三者叠加让「从零建库→跑通首页→提交答辩」全程可预期、可回溯、可截图。尤其对课程设计刚结束、Python 基础尚可但 Web 工程经验为零的同学它比“在线考试系统”更贴近生活场景比“图书管理系统”更能练透 auth 模块与中间件机制——真正把 Django 的 MTV 架构、CBV 视图、模板继承、静态文件管理、admin 后台定制这些“听起来懂、写起来懵”的概念钉死在具体按钮点击、表单提交、页面跳转的每一帧里。这不是玩具项目是能放进简历 GitHub 仓库、经得起老师现场提问的最小可行产品。2. 用 PyCharm 搭建 Django MySQL 开发环境从新建项目到数据库连通的最小闭环2.1 创建虚拟环境并初始化 Django 项目PyCharm 内置流程PyCharm 社区版或专业版均可完成关键在于隔离依赖。新建项目时务必勾选 “New environment using Virtualenv”Python 解释器选择你已安装的 Python 3.8避免用系统自带 Python防止权限冲突。项目创建后在 PyCharm 底部 Terminal 中执行pip install django4.2.7 mysqlclient2.2.4注意mysqlclient是 Django 连接 MySQL 的官方推荐驱动不是pymysql后者需额外配置django.db.backends.mysql的OPTIONS字段启用pymysql.install_as_MySQLdb()。版本锁定4.2.7是因 Django 4.2.x 对 Python 3.11 兼容性最稳且长期获得安全更新mysqlclient 2.2.4在 macOS Monterey / Ubuntu 22.04 / Windows 10 上编译成功率最高。若pip install mysqlclient报错mysql_config not found请先安装 MySQL 开发头文件Ubuntu 执行sudo apt-get install libmysqlclient-devmacOS 执行brew install mysql-clientWindows 请下载 mysqlclient 官方 wheel 并pip install xxx.whl。2.2 配置 settings.py让 Django 认出你的 MySQL 数据库打开settings.py定位到DATABASES配置段替换为以下内容请严格按注释修改DATABASES { default: { ENGINE: django.db.backends.mysql, # 必须是 mysql不是 sqlite3 NAME: travel_forum, # 数据库名需提前在 MySQL 中 CREATE DATABASE travel_forum CHARACTER SET utf8mb4; USER: root, # MySQL 用户名建议不用 root用单独账号见 2.3 PASSWORD: your_mysql_password, # 对应用户的密码 HOST: 127.0.0.1, # 不要用 localhostUnix socket 问题用 127.0.0.1 强制走 TCP PORT: 3306, # 默认端口若改过请同步 OPTIONS: { charset: utf8mb4, # 关键支持 emoji 和四字节 UTF-8 init_command: SET sql_modeSTRICT_TRANS_TABLES, # 避免 MySQL 严格模式报错 }, TEST: { CHARSET: utf8mb4, COLLATION: utf8mb4_unicode_ci, } } }逻辑说明HOST设为127.0.0.1而非localhost是血泪经验——MySQL 在 Unix 系统下将localhost解析为 socket 连接而mysqlclient默认走 TCP不匹配直接报错Cant connect to local MySQL server through socket /tmp/mysql.sock。charset和init_command是为解决中文乱码和Data truncated for column xxx类型警告的刚需配置。TEST字段确保单元测试时字符集一致。2.3 在 MySQL 中创建专用数据库与用户安全基线实践不要用 root 账号跑 Django 应用在 MySQL 命令行中执行-- 创建数据库utf8mb4 支持 emoji CREATE DATABASE travel_forum CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 创建专用用户假设本地开发密码设为 travelpass123 CREATE USER travel_userlocalhost IDENTIFIED BY travelpass123; -- 授予该用户对 travel_forum 数据库的全部权限 GRANT ALL PRIVILEGES ON travel_forum.* TO travel_userlocalhost; -- 刷新权限 FLUSH PRIVILEGES;然后将settings.py中的USER和PASSWORD改为travel_user和travelpass123。这步看似多此一举但能避免未来部署时因权限过大被安全审计打回也杜绝了DROP DATABASE误操作波及其他项目。2.4 执行迁移并验证连接看到OK才算真正打通在 PyCharm Terminal 中依次执行# 生成初始迁移文件auth、contenttypes 等 Django 内置表 python manage.py makemigrations # 将迁移应用到 MySQL此时会创建数据表 python manage.py migrate # 创建超级用户用于登录 admin 后台 python manage.py createsuperuser若migrate输出类似Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK ...说明 Django 已成功连接 MySQL 并建表。此时访问http://127.0.0.1:8000/admin/用createsuperuser创建的账号登录能看到 Django admin 界面——这是整个系统最硬的「第一块砖」后续所有模型、视图、模板都基于此延伸。3. 设计旅游攻略论坛的核心数据模型从景点、帖子、评论到用户权限分级3.1 用户扩展模型为什么不用默认 User而要 ProfileDjango 自带auth.User模型只含username,email,password等基础字段但旅游论坛需要avatar头像、bio个人简介、location常驻城市、travel_experience旅行年限等属性。直接继承AbstractUser会破坏 admin 用户管理逻辑最佳实践是用 OneToOneField 关联扩展# models.py from django.db import models from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver class Profile(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE, related_nameprofile) avatar models.ImageField(upload_toavatars/, blankTrue, nullTrue, defaultavatars/default.png) bio models.TextField(max_length500, blankTrue) location models.CharField(max_length100, blankTrue) travel_experience models.PositiveSmallIntegerField(default0) # 单位年 def __str__(self): return f{self.user.username}\s profile # 自动创建 Profile 实例 receiver(post_save, senderUser) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(userinstance) receiver(post_save, senderUser) def save_user_profile(sender, instance, **kwargs): instance.profile.save()参数说明upload_toavatars/表示文件存入media/avatars/目录需在settings.py中配置MEDIA_ROOT和MEDIA_URLdefaultavatars/default.png要求你提前在media/avatars/下放一张default.pngrelated_nameprofile允许通过user.profile访问扩展信息比user.get_profile()更符合 Django 习惯。3.2 景点Destination与攻略帖Post模型一对多关系的落地细节旅游论坛的核心是「地点」与「内容」。一个景点可有多个攻略帖一个帖子只属于一个景点class Destination(models.Model): name models.CharField(max_length100, uniqueTrue) # 如“敦煌莫高窟” country models.CharField(max_length50) # 国家 province models.CharField(max_length50, blankTrue) # 省/州 city models.CharField(max_length50) # 城市 description models.TextField(blankTrue) created_at models.DateTimeField(auto_now_addTrue) class Meta: ordering [-created_at] verbose_name 景点 verbose_name_plural 景点 def __str__(self): return self.name class Post(models.Model): author models.ForeignKey(User, on_deletemodels.CASCADE, related_nameposts) destination models.ForeignKey(Destination, on_deletemodels.CASCADE, related_nameposts) title models.CharField(max_length200) content models.TextField() # 实际项目中建议用 django-ckeditor 替代原生 TextField image models.ImageField(upload_toposts/, blankTrue, nullTrue) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) is_published models.BooleanField(defaultTrue) # 草稿/发布状态 class Meta: ordering [-created_at] verbose_name 攻略帖 verbose_name_plural 攻略帖 def __str__(self): return f{self.title} - {self.destination.name}关键设计点related_name是灵魂——author.posts可查某用户所有帖子destination.posts可查某景点所有攻略避免Post.objects.filter(authoruser)这类低效查询is_published字段实现软删除与草稿功能比物理删除更安全ordering [-created_at]让Post.objects.all()默认按时间倒序省去每次.order_by(-created_at)。3.3 评论Comment与点赞Like模型轻量级交互的范式评论需关联用户、帖子、父评论支持嵌套回复点赞需防重复class Comment(models.Model): author models.ForeignKey(User, on_deletemodels.CASCADE, related_namecomments) post models.ForeignKey(Post, on_deletemodels.CASCADE, related_namecomments) parent models.ForeignKey(self, on_deletemodels.CASCADE, nullTrue, blankTrue, related_namereplies) content models.TextField() created_at models.DateTimeField(auto_now_addTrue) class Meta: ordering [created_at] class Like(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, related_namelikes) post models.ForeignKey(Post, on_deletemodels.CASCADE, related_namelikes) created_at models.DateTimeField(auto_now_addTrue) class Meta: unique_together (user, post) # 关键联合唯一约束防止同一用户对同一帖子重复点赞 def __str__(self): return f{self.user.username} likes {self.post.title}避坑提示unique_together (user, post)是点赞去重的数据库级保障比在视图里if not Like.objects.filter(userrequest.user, postpost).exists():更可靠parent字段用ForeignKey(self)实现自引用支持无限层级回复实际前端控制 2~3 层即可related_namereplies让comment.replies.all()获取所有子评论语义清晰。3.4 权限模型用 Django Groups 实现版主与管理员分离Django 自带auth.Group完全满足论坛权限需求无需重写 RBAC# 在 Django admin 中手动创建两个 Group # Group Name: Moderator, Permissions: # - posts | post | Can change post # - posts | post | Can delete post # - posts | comment | Can change comment # - posts | comment | Can delete comment # Group Name: AdminStaff, Permissions: # - auth | user | Can add user # - auth | user | Can change user # - auth | group | Can change group # - (其他 admin 后台所需权限)落地技巧在视图中判断权限用request.user.groups.filter(nameModerator).exists()比user.has_perm(posts.change_post)更直观在PostAdmin中可限制版主只能编辑自己发布的帖子管理员才能编辑所有——这才是真实业务场景的权限粒度。4. 避坑Django MySQL 开发中最常翻车的 5 个硬核问题与解法4.1 现象OperationalError: (1366, Incorrect string value: \\xF0\\x9F\\x92\\x96... for column content at row 1)原因MySQL 表/列字符集未设为utf8mb4导致 emoji 或某些生僻汉字插入失败。即使settings.py设了charsetutf8mb4数据库、表、列三级字符集仍可能为utf8MySQL 的utf8实为utf8mb3。解决登录 MySQL执行ALTER DATABASE travel_forum CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;查看所有表SELECT table_name FROM information_schema.tables WHERE table_schema travel_forum;对每个表执行ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;重启 Django 开发服务器。4.2 现象PyCharm 中manage.py runserver启动后浏览器访问http://127.0.0.1:8000显示ModuleNotFoundError: No module named mysite.urls原因PyCharm 新建项目时Django 项目名如mysite与settings.py中ROOT_URLCONF mysite.urls的路径不一致或urls.py文件被误删。解决检查项目根目录下是否存在mysite/urls.pyDjango 4.2 默认生成若项目名是travel_forum则ROOT_URLCONF应为travel_forum.urls且travel_forum/urls.py必须存在在 PyCharm 中右键项目根目录 →Mark Directory as→Sources Root确保包路径识别正确。4.3 现象上传图片后img src显示 404media/avatars/xxx.jpg找不到文件原因Django 默认不提供MEDIA_URL的静态文件服务仅在DEBUGTrue时可通过django.views.static.serve临时处理但需显式配置 URL 路由。解决在主urls.py如travel_forum/urls.py末尾添加from django.conf import settings from django.conf.urls.static import static urlpatterns [ # ... your other url patterns ] # 仅在 DEBUG 模式下生效生产环境由 Nginx 处理 if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)并在settings.py中添加MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media)确保media/目录存在PyCharm 中右键项目 →New→Directory→media。4.4 现象python manage.py migrate报错django.db.utils.ProgrammingError: (1146, Table travel_forum.django_migrations doesnt exist)原因MySQL 数据库为空但 Django 试图读取django_migrations表记录迁移状态而该表尚未创建。常见于首次migrate前手动清空了数据库或删了migrations/文件夹。解决删除所有migrations/0*.py文件保留__init__.py执行python manage.py makemigrations --empty auth强制为 auth 应用生成空迁移手动编辑生成的0001_initial.py将dependencies []改为dependencies [(auth, 0001_initial)]执行python manage.py migrate --fake-initial对已存在表标记为已迁移再执行python manage.py migrate。4.5 现象Post.objects.filter(destination__name__icontains敦煌)查询极慢10 万条数据耗时 3s原因icontains使用LIKE %敦煌%MySQL 无法使用索引全表扫描。解决为destination.name添加数据库索引在Destination模型中加db_indexTruename models.CharField(max_length100, uniqueTrue, db_indexTrue)执行python manage.py makemigrations python manage.py migrate若需前缀搜索如startswith可考虑 MySQL 5.7 的FULLTEXT索引 MATCH AGAINST但icontains场景下db_indexTrue已足够提速 5~10 倍。5. 实现「用户发帖后自动通知关注者」用 Django Signals EmailBackend 的轻量方案5.1 为什么不用 WebSocket——毕业项目的务实选择标题里提到的 “django websocket 实现后台有数据前端推送”对毕设而言是典型的“技术炫技陷阱”。WebSocket 需要额外部署daphne或uvicorn配置channels写consumers.py前端还要接WebSocket对象——而一个旅游论坛的真实需求是当用户 A 发布敦煌攻略所有关注了“敦煌”景点的用户 B/C/D 收到一封邮件提醒。邮件通知的 ROI投入产出比远高于实时推送它不增加服务器长连接压力不依赖前端在线状态代码量少且 Django 内置send_mail完全胜任。5.2 用 Signal 监听 Post 创建事件并触发邮件发送在posts/apps.py中定义 AppConfig# posts/apps.py from django.apps import AppConfig class PostsConfig(AppConfig): default_auto_field django.db.models.BigAutoField name posts def ready(self): import posts.signals # 导入 signals 模块触发注册在posts/signals.py中编写监听逻辑# posts/signals.py from django.db.models.signals import post_save from django.dispatch import receiver from django.core.mail import send_mail from django.conf import settings from .models import Post, Destination receiver(post_save, senderPost) def notify_followers_on_new_post(sender, instance, created, **kwargs): if not created or not instance.is_published: return # 只处理新创建且已发布的帖子 # 获取关注该景点的所有用户假设已实现 Follow 模型 # 此处简化假设有 Follow 模型结构为 user, destination, created_at try: from .models import Follow followers Follow.objects.filter(destinationinstance.destination).select_related(user) follower_emails [f.user.email for f in followers if f.user.email] except ImportError: return # 若未实现 Follow 模型跳过通知 if not follower_emails: return subject f新攻略上线{instance.destination.name} - {instance.title} message f 您关注的景点 {instance.destination.name} 有新攻略发布 标题{instance.title} 作者{instance.author.username} 链接http://127.0.0.1:8000/post/{instance.id}/ —— 旅游攻略论坛 send_mail( subjectsubject, messagemessage, from_emailsettings.DEFAULT_FROM_EMAIL, # 在 settings.py 中设为 noreplytravel-forum.com recipient_listfollower_emails, fail_silentlyFalse, # 开发期设为 False便于捕获 SMTP 错误 )关键参数说明fail_silentlyFalse确保邮件发送失败时抛出异常方便调试select_related(user)减少 N1 查询settings.DEFAULT_FROM_EMAIL需在settings.py中配置如TravelForum noreplytravel-forum.localrecipient_list必须是 list不能是 QuerySet。5.3 配置 SMTP 发送邮件本地开发用 Gmail 或 QQ 邮箱在settings.py中添加# 邮件配置开发环境用 Gmail EMAIL_BACKEND django.core.mail.backends.smtp.EmailBackend EMAIL_HOST smtp.gmail.com EMAIL_PORT 587 EMAIL_USE_TLS True EMAIL_HOST_USER your_gmailgmail.com # 替换为你的邮箱 EMAIL_HOST_PASSWORD your_app_password # Gmail 需用「应用专用密码」非登录密码 DEFAULT_FROM_EMAIL EMAIL_HOST_USER血泪经验Gmail 必须开启「两步验证」并生成「应用专用密码」16位QQ 邮箱需在「账户 → POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务」中开启 SMTP 并获取授权码。切勿在代码中硬编码密码应使用os.environ.get(EMAIL_PASSWORD)从环境变量读取。5.4 测试邮件通知用 Django Shell 快速验证在 PyCharm Terminal 中运行python manage.py shell输入 from posts.models import Post, Destination from django.contrib.auth.models import User dest Destination.objects.get(name敦煌莫高窟) user User.objects.get(usernametestuser) Post.objects.create( ... authoruser, ... destinationdest, ... title敦煌深度游莫高窟鸣沙山全攻略, ... contentDay1: ..., ... is_publishedTrue ... ) # 观察终端是否输出 Sent 1 email(s)检查收件箱若收到邮件说明 Signal 链路完全打通。这是毕业答辩时最能体现「工程闭环能力」的亮点——不是堆砌技术名词而是用 20 行代码解决真实用户触达问题。6. 让毕设代码真正「可交付」PyCharm 一键打包 requirements.txt 生成 README 写法6.1 用 PyCharm Terminal 生成精准的 requirements.txt很多人用pip freeze requirements.txt结果导出所有全局包包括pycharm-debugger导致部署时报错。正确做法是只导出项目依赖# 确保在项目虚拟环境中PyCharm 左下角显示 venv 名称 # 1. 先清理无关包 pip uninstall -y django mysqlclient pillow # 卸载再重装确保干净 # 2. 安装项目必需包按 settings.py 和 models.py 依赖推断 pip install django4.2.7 mysqlclient2.2.4 pillow10.2.0 # 3. 生成最小依赖列表 pipreqs . --encodingutf8 --forcepipreqs 优势它静态分析*.py文件中的import语句只提取真实用到的包生成的requirements.txt类似Django4.2.7 mysqlclient2.2.4 Pillow10.2.0无setuptools、wheel等构建工具无pywin32等平台相关包部署时pip install -r requirements.txt100% 成功。6.2 PyCharm 中一键运行所有测试用例避免答辩现场翻车Django 自带TestCase但很多同学写完代码就搁置测试。在 PyCharm 中右键tests.py→Run Unittests in tests.py可快速验证核心逻辑。例如# tests.py from django.test import TestCase from django.contrib.auth.models import User from .models import Destination, Post, Profile class PostModelTest(TestCase): def setUp(self): self.user User.objects.create_user(usernametestuser, password123456) self.dest Destination.objects.create(name九寨沟, country中国, city阿坝) self.post Post.objects.create( authorself.user, destinationself.dest, title九寨沟秋季摄影指南, content最佳时间10月中下旬..., is_publishedTrue ) def test_post_str_method(self): self.assertEqual(str(self.post), 九寨沟秋季摄影指南 - 九寨沟) def test_post_has_author_and_destination(self): self.assertEqual(self.post.author.username, testuser) self.assertEqual(self.post.destination.name, 九寨沟)技巧PyCharm 的测试运行器会显示绿色对勾✅或红色叉❌点击失败用例可直接跳转到断言行。答辩前跑一遍所有测试比口头承诺“功能都测过”更有说服力。6.3 README.md 必写 4 个区块让老师 30 秒看懂你的工作量一份专业的 README 不是装饰是答辩材料的前置说明书。我通常这样写# 旅游攻略论坛系统Django MySQL ## ✅ 功能概览 - 用户系统注册/登录/个人资料页/头像上传 - 景点管理增删改查景点信息国家/城市/描述 - 攻略发布富文本编辑、图片上传、发布/草稿状态 - 互动功能评论支持嵌套回复、点赞、关注景点 - 后台管理Django Admin 定制帖子审核、用户封禁、数据统计 ## ⚙️ 快速启动PyCharm 环境 1. 克隆仓库git clone https://github.com/yourname/travel-forum.git 2. 创建 MySQL 数据库CREATE DATABASE travel_forum CHARACTER SET utf8mb4; 3. 配置 settings.py 中的 DATABASES用户名/密码/主机 4. 终端执行pip install -r requirements.txt → python manage.py migrate → python manage.py createsuperuser 5. 启动python manage.py runserver访问 http://127.0.0.1:8000/admin/ ## 项目结构travel_forum/ # Django 项目根目录 ├── manage.py ├── travel_forum/ # settings.py, urls.py 所在 ├── posts/ # 主应用景点、帖子、评论模型 ├── users/ # 用户扩展、Profile、关注逻辑 ├── static/ # CSS/JS/图片前端资源 ├── media/ # 用户上传文件头像、帖子图 └── requirements.txt## 毕设亮点 - **真需求驱动**所有功能围绕「旅游者找攻略、分享经验」设计非 CRUD 堆砌 - **工程规范**虚拟环境隔离、requirements 精准、测试覆盖率 85%、README 可执行 - **可扩展设计**Signal 解耦通知逻辑Follow 模型预留社交关系为后续加「私信」「收藏夹」留接口最后一句我带过 12 届毕设见过太多同学在答辩最后 5 分钟才打开 PyCharm手忙脚乱调环境、改路径、删 print。而这份 README 里的「快速启动」区块就是你提前写给老师的「信任契约」——它证明你不仅写了代码更理解什么是可交付的软件。希望帮到你。本文还有配套的精品资源点击获取
返回列表