
第一次接到Django作业的时候任务内容其实挺简单用Django做一个教室管理系统能对教室信息做增删改查就行。但真正动手之后我才发现从环境搭建开始就处处是坑——Python版本选错导致Django装不上、App创建完忘了注册、页面里的图片死活加载不出来、本地跑得好好的代码到了部署环节又一堆问题。这篇文章把我第一次交Django作业的完整过程记录下来从项目初始化一直写到waitressnginx部署中间包含了MTV模式的认知拆解、静态文件加载失败的排查链路、ORM查询与删除对象的正确姿势以及一些常规教程里不会写的实战细节。如果你正在做第一个Django项目或者刚学完框架基础想找一份完整实操参考这篇应该能帮你少走不少弯路。1. 环境准备与项目初始化第一次作业的第一道坎1.1 版本选择比想象中重要先说一个最容易被新手忽略的问题Python和Django的版本搭配。我最初图省事直接装了最新版Python然后又用pip装了一个当时最新的Django版本结果建完项目一运行控制台直接报了一堆看不懂的错误。后来才明白很多第三方库、教程代码、甚至编辑器插件都不一定跟得上Django大版本的更新节奏版本跨度太大等于给自己挖坑。如果你的作业没有特殊要求我这里给一套比较稳的组合Python 3.10或3.11配Django 4.2 LTS版本。Django的LTS版本Long Term Support会获得较长时间的安全更新和bug修复网上能查到的资料也最多遇到问题搜一下基本都有答案。创建虚拟环境是另一个容易被跳过的环节。有些同学图方便直接在全局环境里装Django一个项目跑通倒还好以后做第二个项目的时候依赖冲突能把人逼疯。第一次作业虽然只有一个项目但养成用虚拟环境的习惯对你后面做毕业设计或者实习项目都只有好处。# Windows 10环境下操作 python -m venv venv venv\Scripts\activate pip install django4.2激活后命令行前面会出现(venv)的标识看到这个就说明当前环境已经切到虚拟环境里了。这一步做完环境准备阶段才算真正结束。1.2 创建项目到创建App的完整命令Django的项目project和应用app是两个层级不同的概念。项目是整个网站的配置和入口App是项目中一个个具体功能模块。教室管理系统可以算一个App它负责教室信息的全部业务逻辑。# 创建项目project_name 可以换成你自己的项目名 django-admin startproject classroom_project cd classroom_project # 创建App这里叫 classroom python manage.py startapp classroom跑完这两条命令后你会得到一个标准的Django目录结构。很多教程会直接把目录图贴出来让你背但我建议你花十分钟把每个文件打开看一眼理解它们各自管什么settings.py对应项目配置urls.py是路由入口models.py写数据模型views.py写业务逻辑admin.py是后台管理注册的地方。后面所有报错排查时基本都要回到这几个文件里找原因。1.3 注册App与时区设置的坑创建完App之后第一件要做的事是去settings.py里把App注册进去。在INSTALLED_APPS列表中加入classroom这个字符串。第一次作业最常见的报错之一就是忘做这一步结果运行迁移或访问页面时提示类似classroom is not a registered namespace之类的问题。时区和语言配置也建议顺手改掉否则后台管理页面显示的是英文和UTC时间演示效果会打折扣# settings.py LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai USE_TZ True改完这两项中文界面和本地时间就能正常显示。顺便说一句USE_TZ保持True是Django推荐的用法数据库里存UTC时间展示时再转本地时间这套机制对跨时区业务很有用虽然第一次作业用不上但理解这个设定比直接改成False要靠谱。2. 拆解MTV模式作业里绕不过去的框架认知2.1 MTV三个字母各自承担什么Django面试题和作业答辩里几乎必问的一个问题就是MTV模式里的M、T、V各有什么作用。如果你只是背概念很容易在老师追问的时候露馅。我用教室管理系统里的教室列表页来拆解一遍。M是Model对应数据模型在Django里就是models.py中定义的类。比如你定义一个Room类里面写name教室名称、capacity容纳人数、location所在位置这些字段这个类映射到数据库就是一张room表。Model的作用是让你完全不用写SQL语句全靠Python代码操作数据库。V是View对应视图函数或视图类写在views.py里负责业务逻辑。比如获取所有教室并传给模板渲染这个动作就是在View中完成的。它相当于整个系统的大脑接收到用户请求后决定要查哪些数据、做什么处理、最后返回什么内容。T是Template对应模板文件是HTML页面和Django模板语法的混合体。它的职责是展示数据比如用循环语句把教室列表一条条渲染成表格。2.2 一次页面请求在MTV中的完整流转把三个字母串起来的是根路由urls.py和App自己的urls.py。用户访问http://127.0.0.1:8000/rooms/这个地址时Django先看根路由找到匹配的include项再交给classroom这个App的urls处理匹配到对应的视图函数视图函数通过Room.objects.all()从数据库取出数据把数据打包到上下文中调用render渲染模板最终返回一段完整的HTML给浏览器。这个流程建议你画在纸上或者贴在自己桌面上因为后面无论是写代码还是排查问题都离不开这条链路。遇到页面打不开的问题时你可以照着链路逐层检查路由有没有写对、视图函数有没有返回、模板路径对不对每一步都有对应的报错信息。2.3 新手最容易踩的认知误区第一个误区是觉得模板里可以随便写Python逻辑。Django模板语言是有意做简化的不支持复杂的函数调用和业务计算它的定位就是展示层。如果你发现模板里开始写非常复杂的判断逻辑通常意味着这项处理应该放到View里先算好再传过来。第二个误区是忽略Model的设计就急着写页面。第一次作业往往数据库就一张表看不出问题但一旦涉及多个实体、关联查询时前期表结构设计不好后面改起来伤筋动骨。第三个误区是觉得ORM不如SQL高级。有些学过数据库课程的同学喜欢在Django里用raw SQL觉得这样真实。但ORM的价值在于自动处理参数转义、减少SQL注入风险、屏蔽数据库差异这些恰恰是工程实践里最关心的点。作业阶段好好用ORM顺带补一补SQL知识而不是反过来。3. 静态文件加载失败排查vscode里img标签在static文件中显示不了的完整解决过程3.1 问题复现路径明明没错但图片就是不显示教室管理系统里我给每个教室加了一张图片在vscode里写img标签时路径看着没问题浏览器却显示一个破图图标。打开开发者工具F12看Network面板发现图片请求返回404。这个坑在Django新手里出现频率极高原因是很多人把Django的静态文件机制和普通HTML文件的相对路径搞混了。普通HTML页面里写img srcimages/room1.jpg文件路径相对页面位置来解析但Django页面的URL是由路由控制的页面地址跟模板文件在磁盘上的位置没有对应关系你写给相对路径往往指向了一个根本不存在的URL。3.2 排查链路从settings到模板标签我那次排查花了不少时间这里直接把完整链路整理给你按顺序检查基本都能定位问题。第一步检查settings.py里有没有配STATIC_URL。新创建的Django项目默认会有STATIC_URL static/这个配置决定了静态文件的URL前缀比如这里的设置会让静态文件请求以/static/开头。第二步检查静态文件放的位置。Django查找静态文件的默认逻辑是每个App下的static目录。你把room1.jpg放在classroom/static/classroom/images/room1.jpg这个位置注意static目录下还要再套一层classroom目录做命名空间这是为了避免多个App之间静态文件重名冲突。第三步是模板标签。模板文件里不能直接写静态文件路径而是要在文件顶部先加载静态文件模块再用模板标签生成完整URL{% load static %} img src{% static classroom/images/room1.jpg %} alt教室图片{% static %}标签会在渲染时把STATIC_URL拼到路径前面生成/static/classroom/images/room1.jpg这个完整地址。第四步如果前几步都做了还不行就检查settings.py里STATICFILES_DIRS配置。这个配置用于额外指定一些App之外的静态文件目录比如项目根目录下一个统一的static文件夹。第一次作业用不到这个配置但也别乱加加错路径会导致静态文件服务异常。3.3 容易被忽略的细节vscode里打开模板文件时编辑器可能会提示static标签高亮或者报错这是正常的因为vscode默认不识别Django模板语法。安装一个Django插件比如Django模板语法高亮插件能改善编辑体验但插件的提示不一定代表运行时就报错一切以浏览器实际效果为准。还有一个细节是DEBUG设置。Django开发服务器runserver只有在DEBUGTrue时才会自动处理静态文件如果你调试时把DEBUG关了静态文件会全部加载失败。第一次作业阶段保持DEBUG默认的True就行到了后文讲部署的时候再处理。如果图片还是不显示教你一个排查技巧直接在浏览器地址栏输入http://127.0.0.1:8000/static/classroom/images/room1.jpg看看能不能访问到。这个地址是静态文件的实际URL如果能访问说明配置没问题问题出在模板标签如果404问题出在文件位置或目录结构。4. 数据模型与数据库操作教室管理系统里的增删改查4.1 把教室变成一张数据表教室管理系统的核心实体就是教室在classroom/models.py中定义Room模型from django.db import models class Room(models.Model): name models.CharField(教室名称, max_length100, uniqueTrue) capacity models.IntegerField(容纳人数, default30) location models.CharField(所在位置, max_length200) has_projector models.BooleanField(是否有投影仪, defaultFalse) created_at models.DateTimeField(创建时间, auto_now_addTrue) def __str__(self): return self.name写完模型之后要生成并执行迁移Django才会在数据库里真正建表python manage.py makemigrations python manage.py migrate这两条命令是作业环节里必考的。第一个命令基于models.py生成迁移文件类似于把表结构变更记录下来第二个命令把变更真正应用到数据库。如果只写模型不执行迁移运行时会报no such table的错误。字段类型也是一个值得展开的点。CharField对应数据库的varcharIntegerField对应intBooleanField对应booleanDateTimeField对应datetime。Django就是靠这些映射关系让你不需要关心具体用的是什么数据库——默认的SQLite在第一次作业里完全够用也免去了安装MySQL的麻烦。4.2 查询与删除对象ORM操作的正确姿势教室列表页需要从数据库取出所有教室这就需要用到ORM查询。Django提供的查询API非常直观常用的查询操作集中在视图里from .models import Room # 查询所有教室按容量降序排列 room_list Room.objects.all().order_by(-capacity) # 筛选指定位置的教室 building_a_rooms Room.objects.filter(location__startswithA栋) # 查询单个教室不存在会抛出 DoesNotExist 异常 room Room.objects.get(pk1)删除对象有两种方式这是任务里执行查询-删除对象对应的核心内容。第一种是拿到单个对象后调用delete方法room Room.objects.get(pk1) room.delete()第二种是通过查询集批量删除Room.objects.filter(capacity__lt20).delete()这里有几点值得注意。get()方法在查询结果不存在或多于一条时都会抛出异常所以更稳妥的做法是配合try/except使用或者在明确知道只有一条记录时才用。filter()返回的是QuerySet就算没有匹配记录也只会返回空QuerySet不会抛异常所以在列表页展示场景下优先用filter。还有一个新手经常问的问题删除数据还能恢复吗ORM的delete执行的是真正的数据库DELETE如果没有事务回滚或备份机制删了就没有了。所以作业演示删除功能之前记得先备份数据或者干脆用SQLite数据库文件复制一份。另外顺带提一句教室管理系统django这个需求里经常会出现的权限控制问题。有的作业要求区分学生和管理员角色这个功能用到的是Django自带的认证系统模型层面涉及的是另一个概念RBAC基于角色的访问控制。Django的默认权限框架已经是一套轻量的RBAC实现Admin后台的分组和权限管理就是它的直接应用。第一次作业把这个机制讲清楚能加分不少但不建议自己闭门造车地乱加权限字段——Django已经给了轮子直接用就好。4.3 Admin后台作业演示的加分利器Django内置的Admin后台是我特别建议你在作业里展示的功能它几乎零成本却能明显提升作业的完成度。在classroom/admin.py中注册模型from django.contrib import admin from .models import Room admin.register(Room) class RoomAdmin(admin.ModelAdmin): list_display (name, capacity, location, has_projector) list_filter (location, has_projector) search_fields (name, location)然后创建超级管理员账号python manage.py createsuperuser按照提示输入用户名、密码输入时不会显示字符这是正常的。启动开发服务器后访问http://127.0.0.1:8000/admin/就能登录到后台管理界面。你会看到教室数据已经可以被可视化管理了。list_display可以在列表页直接展示字段list_filter提供筛选search_fields开启搜索框。这些配置代码量极少但给老师演示的时候效果非常直观——说明你不只是会照抄教程还理解了Django后台管理的基本运作方式。如果你作业里还要求做图表、统计等功能后台管理作为数据查看入口也能省下不少自己写页面的功夫。5. 从能跑到能演示waitressnginx部署的取舍与实操5.1 为什么本地runserver不够用作业做到这里功能已经完整了。但如果只是本地启动python manage.py runserver演示你可能会遇到尴尬情况老师或助教让你把项目跑起来你只能在自己电脑上跑或者局域网里别人访问你的电脑runserver的性能通常也不太够用。runserver是Django自带的开发服务器它在编码、调试、报错提示方面做了优化但并没有做高并发处理。生产环境部署一般需要两步用WSGI服务器比如gunicorn、uwsgi或waitress运行Django应用再用nginx做反向代理对外接收请求、转发给WSGI服务器、托管静态文件。这个架构不是过度设计而是因为一次页面请求里有大量细节不适合让Django进程直接处理静态文件读写磁盘、请求头解析、并发连接管理等。如果你用的是Windows 10部署工具的选择就得注意一下。gunicorn在Windows上支持不友好很多教程里默认Linux环境你照搬会发现装都装不上。我之前用的是waitress一个纯Python写的WSGI服务器跨平台表现优秀安装简单性能也够用。5.2 Windows10下waitress部署的完整步骤先把waitress装进虚拟环境pip install waitress然后启动服务等待Python应用监听某个端口waitress-serve --listen127.0.0.1:8000 classroom_project.wsgi:application这条命令指定了监听地址和端口后面跟的是WSGI应用入口。classroom_project是项目名wsgi是项目下的wsgi.py文件application是Django默认创建的WSGI应用对象。启动成功后本机访问http://127.0.0.1:8000就能正常打开你写的教室管理系统了。我遇到过一个问题Windows控制台编码不是UTF-8waitress输出日志里的中文全变成乱码看着吓人但服务实际是正常的。你可以在启动命令前设置环境变量set PYTHONUTF81或者直接用系统自带的运行窗口、在vscode终端里配置默认编码为UTF-8。这个问题不影响功能但日志都是乱码会给排查问题带来很大困扰提前做一次设置能省不少心。5.3 nginx反向代理配置与常见误区waitress跑起来之后系统对外还只开放了一个本地端口。对外提供访问主要有两种方式直接在waitress上监听公网IP或者前面放一个nginx做反向代理。第一个方案简单但静态文件的性能、日志访问、多站点支持都不好我建议直接用nginx。安装nginx并解压后修改nginx/conf/nginx.conf文件里的server块server { listen 80; server_name your_server_ip_or_domain; location /static/ { alias D:/path/to/classroom_project/staticfiles/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }location /static/这一段是让nginx直接处理静态文件请求不再转给Django能明显减轻Python进程的负担。但要让静态文件出现在staticfiles目录需要先运行一次collectstatic命令把各个App下零散的静态文件收集到一起# settings.py 中先配置 STATIC_ROOT BASE_DIR / staticfilespython manage.py collectstatic看到这里可能有同学会问Django本身不是也能处理静态文件请求吗是的但那是开发服务器为了方面调试而附带的福利效率不够让nginx这种专用服务器处理静态文件是web应用的常规做法。多次踩坑之后的建议是改完nginx配置一定要执行nginx -t测试配置文件语法然后再执行nginx -s reload重新加载。直接改完就重启nginx配置写错了会启不来而且排查起来比较绕。还要留意一个坑80端口容易被其他程序占用尤其是本机测试环境里系统服务经常占用80。监听不了就换一个端口比如listen 8080访问时带上端口号即可。Waitress默认只进行单进程处理教室管理系统这种小项目完全没有问题。生产实践中单进程WSGI服务器是常见基础形态本身没有错。如果哪天你的项目访问量上来了再考虑多进程配合进程管理工具来跑waitress思路也是一样的。6. 第一次作业复盘交完作业后我建议你做的几件事6.1 把代码整理成能提交的状态作业验收看的不只是功能演示代码本身的规范程度同样影响评分。我第一次交作业的时候代码文件散落在桌面和U盘里注释几乎没有能跑但老师让看他完全没法读。第二次我在交之前做了三件小事第一用git把项目初始化并提交了一次版本commit信息写清楚比如完成教室信息增删改查功能第二在项目根目录添加requirements.txt这个文件记录全部依赖项换电脑部署时执行pip install -r requirements.txt就能一次性装好所有包pip freeze requirements.txt第三补一份README.md写清项目简介、运行方式、默认账号信息和数据库说明并归档好作业报告需要的截图。这三步总共花不了半小时却是区分做完和做好的关键。6.2 代码组织与注释习惯很多Django新手有个毛病所有业务逻辑全堆在views.py里一个视图函数几百行。教室管理系统规模小这样写看起来问题不大但老师如果问了如果再加一个预约功能你的代码要怎么组织你就尴尬了。合理的做法是查询逻辑封装在Model的Manager或模型方法里视图保持薄薄一层模板公共部分抽成base.html通过{% extends %}和{% block %}复用URL命名使用语义化的name参数比如path(rooms/, views.room_list, nameroom_list)这样模板里的{% url room_list %}就不会写死路径以后改路由也不影响页面链接。教室管理系统里还有一类代码容易写出问题——大量重复的try/except。比如每次查询都手动捕获DoesNotExist异常然后返回错误信息。建议你自定义一个公共函数来处理这类情况或者直接用Django内置的get_object_or_404减少样板代码也降低漏处理异常的概率。6.3 我给第一次作业踩坑清单做了一张表下面这张表是我从自己作业过程里总结出来的高频问题每个后面都附上了解决方向你也可以当验收清单来用问题表现常见原因处理方式运行迁移提示找不到表没有执行makemigrations/migrate先执行makemigrations再migrate页面404URL路由没配置或顺序不对检查根路由include和app内urls配置图片等静态资源404忘了{% load static %}或路径不存在用{% static %}标签检查static目录结构Admin后台登录报错没有创建超级用户执行createsuperuser注意密码规则数据中文显示成乱码Windows控制台编码问题设置PYTHONUTF81或使用UTF-8编码局域网/部署后样式丢失静态文件服务方式不对配置STATIC_ROOT并执行collectstaticnginx托管static修改models.py后出现字段默认值提示已有数据表新增非空字段按提示设置default或nullTrue这张表并不能覆盖所有情况核心思路其实就一个遇到报错不要慌先看错误信息定位到哪一层再对照MTV链路去找问题根源。大多数Django新手报错问题都出在路由、settings配置和目录结构上而不是框架本身。第一次做Django作业最容易产生的错觉是抄一遍教程代码就算学会了。其实真正让你记住知识的是配置环境时踩过的版本坑、排查静态文件时的思路调整、部署时对Web架构的重新理解。教室管理系统虽然只是入门小项目但当你能解释清楚一次页面请求从哪里来、到哪里去、每个环节做了什么的时候你的Django就算是真正入门了。后续想深入可以试试把RBAC权限控制整合进来或者给教室管理系统加上课程表关联、借用审批流程这类真实场景功能代码量不多但对理解框架的设计哲学非常有帮助。