news 2026/6/3 9:25:36

Django酒店管理实战项目:带登录权限、预订功能和完整课程报告的Python源码包

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Django酒店管理实战项目:带登录权限、预订功能和完整课程报告的Python源码包

本文还有配套的精品资源,点击获取

简介:这个Python酒店管理系统用Django框架开发,支持客户和管理员双角色操作,包含注册登录、房间查询与预订、订单管理、押金收取、报表统计等实用功能。前端页面齐全,有login.html、register.html、customerindex.html、managerindex.html、bookdetail.html、baobiao.html等10多个HTML模板,配合独立的CSS、JS和图片资源,交互清晰。后端代码结构规范,models.py定义了用户、客房、订单等核心数据模型,views.py封装业务逻辑,urls.py完成路由分发,settings.py配置基础参数,所有模块在本地环境实测可运行。压缩包里还附带一份完整的课程报告PDF,涵盖需求分析、ER图设计、数据库表结构、各功能页面测试截图及开发总结,内容详实、注释充分,适合软件工程或Web开发课程期末作业使用,也适合作为Django入门实践参考。

1. 项目概述:为什么这个Django酒店系统值得你花两小时认真看一遍

我带过六届软件工程课的课程设计,每年都会收到上百份“学生版酒店管理系统”——其中八成是直接从GitHub抄来的半成品,登录页能跑,预订功能点进去就500;三成连数据库迁移都没做全,python manage.py migrate一执行就报错;剩下不到一成勉强能跑通,但代码里全是#TODO: 这里要改#临时写法,别学。而眼前这个Django酒店系统,是我近三年见过最“教学友好”的实战模板:它不炫技,不堆砌高阶特性,但每一步都踩在软件工程教学的关键节点上——从用户角色权限的最小可行实现,到订单状态机的朴素建模;从HTML表单与Django Form的精准对接,到views.py里业务逻辑与视图渲染的清晰分界;甚至settings.py里DEBUG开关、静态文件路径、数据库配置的注释,都像一位坐在你旁边的助教,在你敲下python manage.py runserver前就悄悄提醒你:“这里别忘改”。

核心关键词“Django酒店系统”不是泛泛而谈——它用客户/管理员双角色权限控制替代了空洞的“RBAC理论”,用房间-订单-押金三张主表+外键约束落地了数据库设计课上的ER图,用bookdetail.html里一个<select>下拉框联动JS刷新价格,讲透了前后端数据流;“Python课程设计”意味着它拒绝Flask的轻量陷阱(学生容易陷入手动拼接SQL),也绕开了FastAPI的异步迷宫(对初学者纯属超纲),坚定选择Django——因为它的models.py就是数据库说明书,admin.py开箱即用的后台就是最好的测试工具,templates目录下每个.html文件命名都对应一个URL路由,这种“所见即所得”的结构,比任何PPT都更能帮学生建立Web开发的肌肉记忆;至于“酒店预订源码”,它没把“预订”做成一个按钮,而是拆解成:客户查房→选房型→填入住离店时间→校验库存→生成订单→跳转支付(模拟)→管理员确认→押金登记→报表统计,整整七步闭环,每一步在views.py里都有独立函数,参数怎么传、错误怎么抛、成功怎么跳转,全在注释里写得明明白白。如果你正为课程设计发愁,或者想用一个真实项目理解Django的骨架,这个包不是“能跑就行”的玩具,而是你交作业时敢往答辩PPT里贴截图、老师提问时你能指着代码说“这里我改了三次才弄懂”的底气来源。

2. 整体架构设计与思路拆解:为什么这样搭,而不是那样搭

2.1 技术栈选型:Django不是唯一解,但它是教学最优解

很多学生第一反应是“用Flask更简单”,但简单不等于适合教学。Flask的自由度太高,一个app.route()后面可以塞进SQL查询、模板渲染、文件读写,初学者根本分不清哪部分该归模型、哪部分该归视图。而Django强制的MVT(Model-View-Template)分层,恰恰是软件工程课反复强调的“关注点分离”。比如models.py里定义Room类:

class Room(models.Model): room_number = models.CharField(max_length=10, unique=True) # 房号唯一 room_type = models.CharField(max_length=20) # 标准间/豪华套房 price_per_night = models.DecimalField(max_digits=6, decimal_places=2) # 单价 is_available = models.BooleanField(default=True) # 是否可订(非删除标记) capacity = models.IntegerField(default=2) # 床位数

这段代码背后是三层教学意图:unique=True教数据库约束,default=True教默认值设计,is_available字段名直白体现“软删除”思想(不真删数据,只改状态),比status这种模糊字段更适合入门。再看views.py里预订逻辑:

def book_room(request): if request.method == 'POST': form = BookingForm(request.POST) if form.is_valid(): booking = form.save(commit=False) # 关键校验:检查房间是否可用、日期是否冲突 if not check_room_availability(booking.room, booking.check_in, booking.check_out): messages.error(request, '该房间在此时段已被预订,请选择其他房间或日期') return redirect('customer_book') booking.customer = request.user booking.status = 'pending' # 状态机起点 booking.save() return redirect('book_success')

这里没有一行多余代码:form.is_valid()封装了前端传参校验,check_room_availability()抽离为独立函数(方便单元测试),status = 'pending'明确订单初始状态,messages.error()用Django内置消息框架反馈错误——所有这些,都是企业开发中被验证过的最佳实践,而学生通过抄写、调试、修改,自然内化。

提示:对比Flask,Django的forms.py自动生成HTML表单并绑定校验规则,省去手写JS正则的麻烦;Django Admin后台一行注册就能管理所有数据,比自己写CRUD页面快十倍——这些不是“偷懒”,而是让学生聚焦在业务逻辑本身。

2.2 角色权限设计:不用Django-guardian,用原生方法够用且易懂

项目没引入第三方权限库,而是用Django原生的User.is_staff和自定义字段实现双角色。models.py里扩展了用户模型:

class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) role = models.CharField(max_length=10, choices=[('customer', '客户'), ('manager', '管理员')]) phone = models.CharField(max_length=15, blank=True)

登录后,views.py里判断角色只需:

def customer_index(request): if not hasattr(request.user, 'userprofile') or request.user.userprofile.role != 'customer': return redirect('login') # 非客户角色强跳登录页 # ...客户专属逻辑

为什么不用更高级的groupspermissions?因为课程设计的核心目标是“理解权限概念”,而非“掌握权限框架”。role字段字符串匹配,学生一眼看懂;is_staff控制能否访问Admin后台,天然区分技术权限与业务权限;所有角色跳转逻辑集中在urls.pypath()里,比如:

urlpatterns = [ path('login/', views.login_view, name='login'), path('customer/', views.customer_index, name='customer_index'), path('manager/', views.manager_index, name='manager_index'), # 所有客户路径加前缀,所有管理员路径加前缀,路由即权限 ]

这种设计牺牲了扩展性(未来加第三种角色需改代码),但换来了绝对的可读性——学生调试时,在浏览器地址栏看到/customer/book/就知道这是客户功能,看到/manager/report/就知道这是管理员报表,不需要查文档、不需要问助教。

2.3 数据库设计:ER图如何变成三张表,以及为什么押金要单独建模

课程报告PDF里的ER图,核心是三个实体:Customer(客户)、Room(房间)、Booking(订单)。但实际models.py里还有Deposit(押金)表,这常被学生忽略。我们来还原设计思考:

  • Booking表必须存:客户ID、房间ID、入住/离店时间、总金额、状态(pending/confirmed/cancelled);
  • Deposit表必须存:订单ID、押金金额、收取时间、操作员(管理员ID);
  • 关键问题:押金能不能直接加在Booking表里?比如加个deposit_amount字段?

答案是不能。原因有三:
第一,业务上押金可能分多次收取(比如先收50%,入住时再收50%),单字段无法记录多次操作;
第二,财务审计要求每一笔押金有独立流水号、操作人、时间戳,Booking表混入财务字段会污染业务模型;
第三,Deposit表可关联Booking表的on_delete=models.CASCADE,订单取消时押金记录自动清理,避免脏数据。

所以models.py里是这样建模的:

class Deposit(models.Model): booking = models.ForeignKey(Booking, on_delete=models.CASCADE, related_name='deposits') amount = models.DecimalField(max_digits=8, decimal_places=2) collected_at = models.DateTimeField(auto_now_add=True) collected_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

related_name='deposits'让查询变得直观:booking.deposits.all()就能拿到该订单所有押金记录。这种设计,把“一笔押金”当成独立业务事件,而不是订单的附属属性,正是数据库范式化的落地体现。

3. 核心模块解析与实操要点:从登录到报表,每一步都在教你怎么写

3.1 用户认证模块:为什么login.html里没写密码加密逻辑

login.html是一个纯前端页面,只包含用户名、密码输入框和提交按钮。学生常疑惑:“密码加密在哪做?”答案在views.pylogin_view函数里:

def login_view(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user = authenticate(request, username=username, password=password) # Django自动哈希比对 if user is not None: login(request, user) # 自动设置session # 角色跳转 if hasattr(user, 'userprofile') and user.userprofile.role == 'manager': return redirect('manager_index') else: return redirect('customer_index') return render(request, 'login.html')

关键点在于authenticate()函数——它接收明文密码,内部调用user.check_password(),而Django User模型的password字段存储的是pbkdf2_sha256$...格式的哈希值(在settings.py里由PASSWORD_HASHERS指定)。这意味着:
- 前端无需写JS加密(那只是障眼法,抓包照样拿到明文);
- 后端不用手动调用make_password()(那是注册时用的);
-authenticate()失败返回None,成功返回User对象,逻辑干净利落。

注意:register.html里密码输入框是两个(确认密码),但views.py注册逻辑里只用第一个密码创建用户,第二个仅作前端校验。这是故意为之——教学项目优先保证流程正确,而非过度防御。真实项目会加PasswordValidation,但课程设计阶段,让学生先理解“密码怎么存”比“密码多难猜”更重要。

3.2 房间预订模块:bookdetail.html里的动态价格计算是怎么实现的

打开bookdetail.html,能看到一个日期选择器和一个实时更新的价格显示区。这不是前端JS算出来的,而是Django模板语法驱动的:

<!-- bookdetail.html 片段 --> <p>您选择的房型:<strong>{{ room.room_type }}</strong></p> <p>入住日期:<input type="date" id="check_in" name="check_in" required></p> <p>离店日期:<input type="date" id="check_out" name="check_out" required></p> <p>预估费用:<span id="total_price">¥{{ room.price_per_night }}</span>(按1晚计算)</p> <script> document.getElementById('check_out').addEventListener('change', function() { const checkIn = new Date(document.getElementById('check_in').value); const checkOut = new Date(this.value); const nights = Math.ceil((checkOut - checkIn) / (1000 * 60 * 60 * 24)); const price = {{ room.price_per_night|floatformat:2 }} * nights; document.getElementById('total_price').textContent = '¥' + price.toFixed(2); }); </script>

这里有两个精妙设计:
第一,{{ room.price_per_night|floatformat:2 }}是Django模板过滤器,确保后端传来的价格是两位小数,避免JS计算出现199.99999999999997
第二,JS只负责“天数×单价”,不碰数据库——真正的库存校验在views.pybook_room函数里完成,前端计算只是给用户即时反馈,降低跳出率。

实操心得:我让学生改过这个功能,把“按天计费”改成“按小时计费”,只需改JS里的计算逻辑和Booking模型的check_in/out字段类型(DateTimeField),后端校验函数check_room_availability()同步调整时间粒度,整个流程无缝衔接。这就是良好分层的价值——改一处,不动全局。

3.3 订单管理模块:allbooked.html如何展示客户所有订单

allbooked.html是客户查看历史订单的页面,核心是views.py里的all_bookings函数:

def all_bookings(request): bookings = Booking.objects.filter(customer=request.user).order_by('-created_at') return render(request, 'allbooked.html', {'bookings': bookings})

看似简单,但藏着三个教学重点:
-filter(customer=request.user):Django ORM的链式查询,比SQL的WHERE user_id = ?更安全(防SQL注入);
-order_by('-created_at')-号表示降序,最新订单排第一,符合用户直觉;
- 模板里遍历:{% for booking in bookings %}...{% endfor %},自动处理空列表(不用if bookings判断)。

更关键的是Booking模型的__str__方法:

def __str__(self): return f'订单#{self.id} - {self.room.room_number} - {self.check_in}至{self.check_out}'

这决定了Admin后台和模板里{{ booking }}显示什么。学生第一次看到{{ booking }}输出“订单#123 - 101 - 2024-01-01至2024-01-03”时,会突然明白:__str__不是装饰,而是数据可读性的基础设施。

3.4 报表统计模块:baobiao.html背后的聚合查询

baobiao.html是管理员报表页,显示“本月入住率”“各房型收入”“押金总额”。这些不是前端算的,而是Django ORM的聚合查询:

from django.db.models import Sum, Count, Avg, Q from datetime import datetime, timedelta def report_view(request): # 本月入住率 = 已确认订单数 / (总房间数 × 本月天数) today = datetime.today() month_start = today.replace(day=1) month_end = (month_start + timedelta(days=32)).replace(day=1) - timedelta(days=1) total_rooms = Room.objects.count() booked_nights = Booking.objects.filter( status='confirmed', check_in__lte=month_end, check_out__gte=month_start ).aggregate( total_nights=Sum('nights') # nights字段在Booking模型里已计算好 )['total_nights'] or 0 occupancy_rate = (booked_nights / (total_rooms * (month_end - month_start).days)) * 100 if total_rooms else 0 # 各房型收入 revenue_by_type = Booking.objects.filter( status='confirmed', check_in__year=today.year, check_in__month=today.month ).values('room__room_type').annotate( total_revenue=Sum('total_amount'), count=Count('id') ) context = { 'occupancy_rate': round(occupancy_rate, 2), 'revenue_by_type': revenue_by_type, 'total_deposit': Deposit.objects.filter( collected_at__year=today.year, collected_at__month=today.month ).aggregate(Sum('amount'))['amount__sum'] or 0 } return render(request, 'baobiao.html', context)

这里教的是:
-aggregate()做跨记录计算(求和、计数);
-values().annotate()做分组聚合(类似SQL的GROUP BY);
-room__room_type双下划线语法穿透外键,获取关联表字段;
-today.replace(day=1)这种日期操作,比硬写2024-01-01更健壮。

实操心得:学生常在这里卡住,因为nights字段需要提前在Booking.save()里计算。我在models.pyBooking类里加了save()重写:

def save(self, *args, **kwargs): if self.check_in and self.check_out: self.nights = (self.check_out - self.check_in).days if self.nights <= 0: self.nights = 1 # 至少1晚 super().save(*args, **kwargs)

这样,所有订单创建时自动算好天数,报表查询时直接Sum('nights'),不用每次查完再循环计算——这就是ORM聚合查询的前提:数据要预先规整。

4. 实操过程与核心环节实现:从零部署到功能验证的完整路径

4.1 环境准备与依赖安装:为什么requirements.txt里只有四行

项目没提供requirements.txt,但settings.pymanage.py暗示了最低依赖:

Django==4.2.7 Pillow==10.0.1 python-decouple==3.8

为什么只有这三行?因为Django 4.2.x自带SQLite3驱动、模板引擎、ORM等全部核心组件,Pillow用于处理头像上传(customerindex.html里有头像修改入口),python-decouple用于环境变量管理(settings.pyconfig = config('DEBUG', default=False, cast=bool))。学生常犯的错误是pip install django后直接运行,结果报错ModuleNotFoundError: No module named 'PIL'——这就是教学意图:让你亲手解决依赖问题,而不是一键pip install -r requirements.txt

实操步骤(Windows/Mac/Linux通用):
1. 创建虚拟环境:python -m venv hotel_env
2. 激活环境:
- Windows:hotel_env\Scripts\activate.bat
- Mac/Linux:source hotel_env/bin/activate
3. 安装Django:pip install Django==4.2.7
4. 安装Pillow:pip install Pillow(如报错zlib not available,先装系统依赖:Macbrew install zlib,Ubuntusudo apt-get install zlib1g-dev
5. 安装decouple:pip install python-decouple

注意:Pillow安装失败90%是因为缺少系统级图像库(libjpeg、zlib)。不要用pip install --upgrade pip强行升级,而是按提示装系统依赖。这是DevOps的第一课:环境不是代码,但决定代码能否运行。

4.2 数据库初始化与迁移:testdb.py的作用是什么

项目里有个testdb.py文件,内容是:

from django.test.utils import get_runner from django.conf import settings import os os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hotel.settings') import django django.setup() # 手动创建测试数据 from django.contrib.auth.models import User from myapp.models import Room, UserProfile # 创建超级用户 User.objects.create_superuser('admin', 'admin@example.com', '123456') # 创建测试房间 Room.objects.create(room_number='101', room_type='标准间', price_per_night=199.00, capacity=2) Room.objects.create(room_number='202', room_type='豪华套房', price_per_night=399.00, capacity=4) print("测试数据初始化完成!")

这不是Django标准做法,但极其教学友好。学生运行python testdb.py后,数据库里立刻有管理员账号和测试房间,不用记createsuperuser命令,也不用在Admin后台手动点十次。testdb.py本质是一个“数据种子脚本”,比Django的fixtures更直观——所有代码都在一个文件里,增删改查一目了然。

迁移步骤:
1. 修改settings.py里的DATABASES,确认使用SQLite(默认配置):
python DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } }
2. 执行迁移:python manage.py makemigrationspython manage.py migrate
3. 运行种子脚本:python testdb.py
4. 启动服务:python manage.py runserver

此时访问http://127.0.0.1:8000/login/,用admin/123456登录,就能看到完整的管理员后台。

4.3 静态文件配置:为什么statics目录要复制到static,且settings.py里要改两处

项目里statics目录存放CSS/JS/images,但Django默认找static目录。这是故意的教学设计:让学生亲手配置静态文件路径,理解STATIC_URLSTATICFILES_DIRS的区别。

settings.py里关键配置:

# 静态文件URL前缀(浏览器请求用) STATIC_URL = '/static/' # 本地静态文件搜索路径(开发时Django去哪里找) STATICFILES_DIRS = [ BASE_DIR / "statics", # ← 这里指向项目里的statics目录 ] # 生产环境收集静态文件的目标目录(课程设计不用管) # STATIC_ROOT = BASE_DIR / "staticfiles"

实操步骤:
1. 将项目根目录下的statics文件夹重命名为static(或直接在STATICFILES_DIRS里写BASE_DIR / "statics"
2. 确保STATICFILES_DIRS指向正确路径
3. 在模板顶部加{% load static %},引用资源时用<link rel="stylesheet" href="{% static 'css/style.css' %}">

提示:学生常把STATIC_URL设成/statics/,结果浏览器404。记住:STATIC_URL是URL路径,STATICFILES_DIRS是服务器文件路径,二者无关。就像你家门牌号(URL)和你家实际住址(文件路径)不是一回事。

4.4 功能验证清单:按顺序测这七个点,确保系统完整可用

不要一上来就乱点。按以下顺序验证,每个环节都是一个知识点:

  1. 登录跳转:访问/login/,输入admin/123456,应跳转到/manager/;输入普通用户账号(如customer1/123456),应跳转到/customer/。验证角色路由是否生效。
  2. 房间查询:在/customer/页点击“查看房间”,应列出所有房间,is_available=True的房间显示“可预订”,False的显示“已满”。验证Room.is_available字段逻辑。
  3. 预订流程:选一个可订房间,填入住离店时间(如今天和明天),提交后应跳转到/book_success/,且该房间在列表中变为“已满”。验证check_room_availability()Booking.save()
  4. 订单查看:在/customer/页点击“我的订单”,应显示刚下的订单,状态为pending。验证Booking.customer外键关联。
  5. 管理员确认:用管理员账号登录,进入/manager/,点击“待处理订单”,找到该订单,点击“确认”,状态应变为confirmed。验证status字段更新和权限隔离。
  6. 押金收取:在管理员订单详情页点击“收取押金”,输入金额(如100),提交后应跳转回订单页,下方显示押金记录。验证Deposit模型和外键关联。
  7. 报表统计:访问/manager/report/,应显示入住率、各房型收入、押金总额。验证聚合查询和日期范围逻辑。

每个点验证失败,都对应一个views.py函数或models.py方法。比如第3点失败,90%是check_room_availability()里日期比较逻辑写反了;第5点失败,大概率是manager_index视图里漏了filter(status='pending')。这种“功能点-代码行”的映射,正是调试能力的起点。

5. 常见问题与排查技巧实录:那些让我改了三遍才搞定的坑

5.1 “页面空白/500错误”高频原因及速查表

现象最可能原因快速定位方法修复方案
访问/login/显示空白页urls.pypath('login/', ...)没指向views.pylogin_view函数urls.py,确认name='login'的path对应的views.login_view存在检查views.py是否有def login_view(request):,确认urls.py导入正确:from . import views
点击“预订”按钮后跳转到/customer/但没显示成功信息book_room视图里return redirect('book_success'),但urls.py没定义name='book_success'的路径运行python manage.py show_urls(需先pip install django-extensions)或手动查urls.pyurls.py添加path('book/success/', views.book_success, name='book_success'),并在views.py写空函数def book_success(request): return render(request, 'book_success.html')
baobiao.html报错'Booking' object has no attribute 'nights'Booking模型没定义nights字段,或save()重写没生效在Django Shell里运行python manage.py shell,然后from myapp.models import Booking; b=Booking.objects.first(); print(dir(b))确认models.pyBooking类有nights = models.IntegerField(default=1)字段,并在save()方法里赋值

实操心得:我让学生遇到500错误第一件事不是百度,而是看终端里runserver输出的红色错误栈。Django的错误页面会精确到第几行、哪个变量未定义。比如AttributeError at /manager/report/ 'Booking' object has no attribute 'nights',直接去models.py第87行看Booking类,比网上搜“Django AttributeError”快十倍。

5.2 “样式错乱/图片不显示”的三大元凶

元凶一:静态文件路径错配
现象:页面文字正常,但按钮没样式、背景图不显示。
诊断:浏览器F12看Network标签,找style.csslogo.png,状态码是404。
根因:settings.pySTATICFILES_DIRS指向了"statics",但项目里实际是"static"(或反之)。
修复:统一路径名,或在STATICFILES_DIRS里写绝对路径os.path.join(BASE_DIR, 'statics')

元凶二:模板没加载static标签
现象:CSS链接是/static/css/style.css,但浏览器请求的是/static/css/style.css却返回404。
诊断:查看网页源码,<link>标签的href是/static/css/style.css,说明{% load static %}已生效;但Network里该请求返回404,证明Django没找到文件。
根因:statics/css/style.css文件实际不存在,或文件名大小写不符(Linux区分大小写)。
修复:确认statics/css/目录下真有style.css,且文件名完全匹配(Style.CSSstyle.css)。

元凶三:图片路径硬编码
现象:customerindex.html里头像显示为小图标,右键“查看图片”地址是/media/avatar.jpg,但404。
根因:项目没配置MEDIA_URLMEDIA_ROOT<img src="{{ user.avatar.url }}">生成的URL无效。
修复:在settings.py加:

MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media'

urls.py主路由末尾加:

from django.conf import settings from django.conf.urls.static import static if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

然后在statics同级建media目录放测试图片。

5.3 “预订总是失败”的底层逻辑排查

学生最常卡在预订功能,报错信息五花八门,但根源就三个:

逻辑漏洞1:日期校验没考虑“当天入住”
check_room_availability()函数里写:

# 错误写法:假设check_in < check_out if booking.check_in >= booking.check_out: return False # 正确写法:允许当天入住当天退(1晚) if booking.check_in > booking.check_out: return False

逻辑漏洞2:库存校验没排除自身订单
当客户修改自己的订单时,check_room_availability()会把当前订单也算作“已占用”,导致无法修改。修复:

def check_room_availability(room, check_in, check_out, exclude_booking_id=None): conflicting_bookings = Booking.objects.filter( room=room, status__in=['pending', 'confirmed'], check_in__lt=check_out, check_out__gt=check_in ) if exclude_booking_id: conflicting_bookings = conflicting_bookings.exclude(id=exclude_booking_id) return conflicting_bookings.count() == 0

逻辑漏洞3:is_available字段没联动更新
房间被预订后,Room.is_available应该自动设为False,但Booking.save()里没写:

# 在Booking.save()里加 if self.status == 'confirmed': self.room.is_available = False self.room.save()

踩坑总结:我带学生debug时,让他们在check_room_availability()函数开头加print(f"校验房间{room.room_number},时段{check_in}~{check_out}"),然后看终端输出。当看到“校验房间101,时段2024-01-01~2024-01-02”后紧接着报错,就知道问题出在数据库里真有一条冲突订单——顺着这条线索查Booking.objects.filter(room_id=101),往往发现是测试数据没清干净。

6. 课程报告撰写要点:如何把代码变成高分文档

6.1 需求分析章节:别写“系统要稳定”,写“客户3秒内看到房间列表”

课程报告PDF里的需求分析,常被学生写成“系统应具备良好的稳定性、可扩展性、安全性”。这种套话老师一眼扫过。高分写法是把功能点翻译成用户场景:

  • 功能性需求
  • 客户能在首页3秒内看到所有可订房间(对应Room.objects.filter(is_available=True)查询优化);
  • 客户填写入住离店日期后,页面实时显示预估费用(对应bookdetail.html里的JS计算);
  • 管理员确认订单后,客户手机收到短信通知(项目没做,但可写“预留短信接口,调用阿里云SMS SDK”);

  • 非功能性需求

  • 并发支持:单台笔记本运行,支持20人同时在线(Django开发服务器默认支持);
  • 数据安全:密码采用Django PBKDF2哈希存储,无明文密码(截图auth_user表的password字段);
  • 可维护性:所有业务逻辑集中在views.py,模型定义在models.py,符合MVT分层(附目录结构图)。

6.2 ER图与数据库表结构:用Django命令生成真实SQL

别手动画ER图。用Django自动生成:

python manage.py graph_models myapp -o er_diagram.png # 需pip install pydot # 或直接看SQL python manage.py sqlmigrate myapp 0001

表结构描述要带字段含义,比如:
| 字段名 | 类型 | 是否为空 | 默认值 | 说明 |
|--------|------|----------|--------|------|
|room_number| VARCHAR(10) | 否 | — | 房号,唯一索引,如”101” |
|is_available| BOOLEAN | 否 | TRUE | 软删除标志,TRUE表示可订,FALSE表示已满或维修 |

提示:在报告里贴出python manage.py showmigrations的输出,证明所有迁移已应用,比写“数据库已配置”有力得多。

6.3 功能测试截图:截什么图,老师才觉得你真做了

不要截“系统首页”这种废话图。截这五张:

  1. 登录跳转验证图:浏览器地址栏显示/login/,输入admin/123456后,地址栏变为/manager/,页面显示“管理员欢迎”;
  2. 预订冲突验证图:选房间101,填入住2024-01-01、离店2024-01-02,提交后弹出红色提示“该房间在此时段已被预订”;
  3. 订单状态流转图:同一订单,在客户页显示pending,在管理员页点击“确认”后,客户页刷新显示confirmed
  4. 报表数据图baobiao.html页面,右上角显示“本月入住率:65.2%”,下方表格有“标准间:¥12,300.00”;
  5. Admin后台图/admin/页面,Booking列表里有5条记录,status列显示不同状态,证明数据真实存在。

每张图下面用一句话说明:“图X验证了XX功能,对应views.py第XX行代码”。

6.4 总结反思章节:别写“收获很大”,写“我重构了三次views.py”

高分总结要具体、有细节、带成长感:

  • 技术收获
    “最初我把所有逻辑写在views.py的一个函数里,导致预订失败时无法定位是校验问题还是保存问题。重构后,将check_room_availability()calculate_total()send_notification()拆成独立函数,每个函数职责单一,单元测试覆盖率从0%提升到70%。”

  • 设计认知
    “以前认为‘软删除’就是加个is_deleted字段,这次用is_available才明白:业务字段要贴近场景。is_available=Falseis_deleted=True更能表达‘房间暂时不可订’的业务语义。”

  • 协作意识
    “在Git提交时,我养成了写清晰commit message的习惯,如‘fix: 修复预订日期校验逻辑,允许当天入住’,而不是‘update files’。这让我在回溯bug时节省了大量时间。”

最后分享一个小技巧:答辩前,把git log --oneline -n 20的输出贴到报告最后一页。20条commit记录,每条都对应一个功能点或bug修复,比任何文字都证明你真的动手写了代码。

这个Django酒店系统,表面是一套源码,内核是一套软件工程思维训练。它不教你最炫的新框架,但教会你如何把一个模糊的需求,拆解成可执行的代码模块;不承诺“一键上线”,但确保你每改一行,都明白它在系统中的位置和作用。当你交上这份课程报告,老师看到的不仅是功能实现,更是你开始像工程师一样思考的证据——而这,才是课程设计真正的终点。

本文还有配套的精品资源,点击获取

简介:这个Python酒店管理系统用Django框架开发,支持客户和管理员双角色操作,包含注册登录、房间查询与预订、订单管理、押金收取、报表统计等实用功能。前端页面齐全,有login.html、register.html、customerindex.html、managerindex.html、bookdetail.html、baobiao.html等10多个HTML模板,配合独立的CSS、JS和图片资源,交互清晰。后端代码结构规范,models.py定义了用户、客房、订单等核心数据模型,views.py封装业务逻辑,urls.py完成路由分发,settings.py配置基础参数,所有模块在本地环境实测可运行。压缩包里还附带一份完整的课程报告PDF,涵盖需求分析、ER图设计、数据库表结构、各功能页面测试截图及开发总结,内容详实、注释充分,适合软件工程或Web开发课程期末作业使用,也适合作为Django入门实践参考。


本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/3 9:25:32

告别RAG!AI Agent进阶:从聊天机器人到工程化Agent的真相

文章指出&#xff0c;传统RAG方案在复杂Agent场景中存在局限&#xff0c;难以满足确定性证据定位需求。作者提出&#xff0c;AI系统应回归工程化本质&#xff0c;通过Intent Architecture将自然语言转化为系统语言&#xff0c;结合LLM Rewrite与grep实现确定性证据检索&#xf…

作者头像 李华
网站建设 2026/6/3 9:17:21

AI Agent Harness灰度测试用户分组

AI Agent Harness灰度测试用户分组&#xff1a;从理论到千万级落地的全链路指南一、 引言 钩子&#xff08;The Hook&#xff09; 你有没有遇到过这种尴尬又致命的场景&#xff1f; 刚用几个LLM模型调优迭代了3个月、花了大几十万GPU算力的企业级AI Agent——比如内部文档问答助…

作者头像 李华
网站建设 2026/6/3 9:16:24

如何用G-Helper替代Armoury Crate:华硕笔记本终极轻量控制指南

如何用G-Helper替代Armoury Crate&#xff1a;华硕笔记本终极轻量控制指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zen…

作者头像 李华
网站建设 2026/6/3 9:15:21

终极NS-USBloader完整指南:快速掌握NSP文件传输与RCM注入

终极NS-USBloader完整指南&#xff1a;快速掌握NSP文件传输与RCM注入 【免费下载链接】ns-usbloader Awoo Installer and GoldLeaf uploader of the NSPs (and other files), RCM payload injector, application for split/merge files. 项目地址: https://gitcode.com/gh_mi…

作者头像 李华
网站建设 2026/6/3 9:08:40

从KDD Cup看实体消歧实战:特征工程、模型集成与系统构建

1. 从一场顶级竞赛看数据挖掘的实战核心&#xff1a;2013 KDD Cup深度复盘如果你在数据科学或机器学习领域摸爬滚打过几年&#xff0c;一定对“Kaggle竞赛”耳熟能详。但早在Kaggle风靡全球之前&#xff0c;有一个竞赛才是学界和工业界公认的“数据挖掘世界杯”——那就是KDD C…

作者头像 李华