ARTICLE DETAIL

资讯详情

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

Django新手入门:一次请求看懂MVT路由视图模板与ORM核心流程

Django新手入门:一次请求看懂MVT路由视图模板与ORM核心流程 Django 新手最容易犯的错是把教程从头到尾翻一遍然后还是不知道从哪下手。我见过不少人在urls.py、views.py、models.py三个文件之间来回切每个文件里的代码单独看都认识一组合就懵。核心原因不是笨而是没抓住主线。Django 的学习主线其实非常清晰浏览器发来一个请求URL 路由先判断这个网址该交给谁处理视图函数负责读取数据、准备内容再把数据交给模板渲染成网页最后返回给浏览器。整个链路就是 路由 → 视图 → 模型 → 模板 → 响应。中间只要涉及数据就通过模型从数据库里取。这篇文章就按这条主线带你把 Django 从零跑通并且告诉你每个环节卡住时优先查什么。1. 抓住 Django 的学习主线一次请求如何走完全程1.1 新手为什么容易在框架里迷路Django 的项目一开始就会生成一堆文件urls.py、views.py、models.py、settings.py、admin.py、forms.py、migrations/目录。新手很容易把它们当成一个个孤立的功能模块去背今天学路由明天学模型后天学模板结果学到一半发现前面全忘了。问题出在缺少一个贯穿始终的“故事线”。Django 本质上做的一件事是接收 HTTP 请求处理业务逻辑返回 HTTP 响应。所有文件都是围绕这件事组织的。你不需要一次性理解全部文件只要跟着一次请求走一遍框架的骨架就立住了。1.2 MVT 三层的真实分工Django 的架构叫 MVT对应模型、视图、模板三部分Model 负责和数据打交道。一张数据库表对应一个模型类增删改查都通过模型操作。View 负责业务逻辑。它接收请求调用模型获取数据决定返回什么内容。Template 负责展示。它只负责把数据渲染成 HTML不写业务逻辑。很多人会把 View 理解为网页页面这是误解。Django 里的 View 更像一个“调度员”页面长什么样由 Template 决定数据从哪来由 Model 决定。把这三个角色的边界划清楚后面写代码就不会乱。1.3 主线与支线对新手来说主线只有一条能跑通一个带数据的网页。支线包括 Admin 后台、用户认证、表单验证、接口开发、部署上线。这些不是不重要而是它们都建立在主线之上。我建议的学习顺序是先跑通一个最简单的 Hello World再逐步加入模板、模型、数据库最后串成一个能增删改查的小功能。不要一上来就研究 Django Rest Framework 或者异步任务那些是你完成主线之后的锦上添花。2. 起步前先搭环境Python、虚拟环境和项目结构2.1 Python 与 Django 的版本匹配Django 是 Python 的第三方库安装前先确认你本机的 Python 版本。打开终端执行python3 --version不同版本的 Django 支持的 Python 版本不一样官方文档里有明确的对应表。整体趋势是 Django 新版要求 Python 3.10 以上如果你用的 Python 比较老需要先升级 Python或者选择一个兼容的 Django 版本。这里有一个容易踩的坑直接用系统自带的 Python 装包很可能会因为系统权限问题报错或者和系统其他工具冲突。特别是 macOS 和 Linux 上系统 Python 往往不是给你项目用的。2.2 用 venv 隔离项目环境虚拟环境是 Django 开发里必须养成的习惯。它可以让每个项目拥有独立的依赖互不污染。Python 自带venv不需要额外安装。创建项目的完整流程# 创建项目目录并进入 mkdir myblog cd myblog # 创建虚拟环境 python3 -m venv venv # 激活虚拟环境 # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate # 安装 Django pip install django # 验证 python -m django --version激活后命令行提示符前面会出现(venv)标记看到这个说明现在用的是项目独立环境。以后每次换终端窗口写代码都要先重新激活虚拟环境这是新手最容易忽略的一步。2.3 创建项目与创建 App很多新手分不清“项目”和“App”。项目是整个网站App 是项目里的一个功能模块。比如一个博客项目里可以有文章 App、评论 App、用户 App。刚开始学先建一个项目、一个 App 就够了。# 创建项目注意后面的点表示在当前目录创建 django-admin startproject config . # 创建 App python manage.py startapp blog创建完之后目录结构大概是这样的myblog/ ├── blog/ # 自定义的 App │ ├── models.py │ ├── views.py │ └── ... ├── config/ # 项目配置文件 │ ├── settings.py │ ├── urls.py │ └── ... ├── manage.py └── venv/项目名叫config还是myproject都行关键是理解这个结构config是项目的总入口blog是具体业务模块manage.py是运行命令的工具。2.4 settings.py 里最该先认识的配置创建完项目后settings.py里几百行配置不需要全都看懂但有几个必须知道INSTALLED_APPS告诉 Django 这个项目启用了哪些 App。新建的blog不会自动加进来必须手动添加否则后面用模型会找不到表。DATABASES数据库配置默认是 SQLite对新手最友好不需要额外安装数据库服务。LANGUAGE_CODE和TIME_ZONE默认是英文和 UTC 时间改成中文和本地时区会更直观。DEBUG开发阶段保持True上线必须改成False并配置ALLOWED_HOSTS。# settings.py 修改示例 INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, blog, # 手动添加 ] LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai配置完成后先跑一下确认整个链路是通的python manage.py migrate python manage.py runserver浏览器打开http://127.0.0.1:8000能看到默认的 Django 欢迎页说明环境基本没问题。这一步是非常好的检查点服务能启动再去写业务代码不要一上来就边写边猜环境有没有问题。3. 主线第一站URL 路由3.1 路由解决什么问题用户访问http://127.0.0.1:8000/post/1时Django 需要知道这个网址应该执行哪段代码。路由就是干这件事的根据 URL 的路径部分找到对应的视图函数。路由文件是项目里的config/urls.py。默认内容大致是这样from django.contrib import admin from django.urls import path urlpatterns [ path(admin/, admin.site.urls), ]这个配置表示访问/admin/时交给 Django 内置的管理后台处理。你需要把其他地址的路由也加到urlpatterns列表里。3.2 path 的用法和动态参数路由最常用的写法是path。它接收两个核心参数路径规则和处理函数。from django.urls import path from blog import views urlpatterns [ path(, views.index, nameindex), path(post/int:pk/, views.post_detail, namepost_detail), ]这里有两个细节要注意表示网站首页地址http://127.0.0.1:8000/。post/int:pk/表示匹配post/1/、post/2/这样的地址int:pk会把 URL 里的数字提取出来转换成整数后传给视图函数参数名叫pk。name是路由的别名。模板里用{% url post_detail pkpost.pk %}生成链接时靠的就是这个名字。好处是地址改了模板不用跟着改。除了int还有str、slug、uuid、path等转换器。新手先记住int和str就够用。3.3 include 拆分路由一个项目会有很多 App如果所有路由都写在config/urls.py里文件会越来越长。Django 提供了include方法可以把路由按 App 拆分。先在blog目录下新建urls.py# blog/urls.py from django.urls import path from . import views urlpatterns [ path(, views.index, nameindex), path(post/int:pk/, views.post_detail, namepost_detail), ]然后在项目的config/urls.py里引入# config/urls.py from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(, include(blog.urls)), ]这样以后新增功能只需要在blog/urls.py里加规则项目入口文件保持干净。注意include的写法是字符串不是直接引入模块名。路由写完后怎么验证我一般先访问地址看能不能打开再看页面显示的内容。如果报了视图不存在优先确认两件事视图函数是否在这个 App 的views.py里视图函数名是否和路由里写的一致。4. 主线第二站视图与模板4.1 视图函数的最小形态路由找到视图后会调用它。视图函数接收一个request参数返回一个HttpResponse对象。最简单的视图长这样# blog/views.py from django.http import HttpResponse def index(request): return HttpResponse(Hello Django)把这个函数和前面的路由配起来访问首页就能看到Hello Django。不要小看这一步这是整个框架跑通的最小闭环。能返回一段文本后面想返回什么内容都是在这个基础上扩展。4.2 请求与响应request对象里装的是用户请求的全部信息常见的有request.method请求方式常见是GET和POST。request.GETURL 参数比如?page2。request.POST表单提交的数据。request.user当前登录用户未登录时是匿名用户。响应的方式有三种按照从简单到复杂排列HttpResponse(文本)直接返回字符串。render(request, 模板.html, {变量: 值})渲染模板并返回页面这是最常用的。redirect(路由名)跳转到另一个地址。新手最容易混的是render和redirect。render返回一个页面redirect让浏览器重新发起一次新请求。表单提交成功后应该用redirect这样刷新页面不会重复提交数据。4.3 模板渲染数据模板文件默认放在 App 下的templates目录里。有一个重要细节模板目录的结构要带 App 名比如blog/templates/blog/index.html。原因是为了防止多个 App 有同名模板时互相覆盖。视图和模板配合的典型写法# blog/views.py from django.shortcuts import render def index(request): name Django 新手 return render(request, blog/index.html, {name: name})模板里使用双大括号输出变量!-- blog/templates/blog/index.html -- !DOCTYPE html html headtitle首页/title/head body h1你好{{ name }}/h1 /body /htmlrender的第三个参数是一个字典字典的键就是模板里能用的变量名。想传多个数据就放在同一个字典里。这一步要理解的是视图准备数据模板负责展示两者通过字典传递分工非常明确。4.4 模板继承与静态文件模板里有一种能力叫“模板继承”它解决的是多页面重复代码的问题。比如每个页面都有相同的头部和底部不需要每个模板都复制一遍。先在blog/templates/blog/base.html里写公共结构!DOCTYPE html html head title{% block title %}默认标题{% endblock %}/title /head body header我的博客/header {% block content %}{% endblock %} footer版权信息/footer /body /html子模板只需要填写自己的内容{% extends blog/base.html %} {% block title %}文章列表{% endblock %} {% block content %} p这里是首页内容/p {% endblock %}静态文件比如 CSS、JS、图片放在 App 下的static目录模板里用{% load static %}和{% static css/style.css %}引用。开发阶段 Django 会自动处理上线时再考虑收集静态文件的问题新手不用过早纠结。判断模板出了问题先看页面是空白还是报错。报错通常会明确告诉你模板文件找不到、变量不存在还是语法错误。空白页面则可能是block名称对不上或者模板里用了错误的条件判断。5. 主线第三站模型与数据库5.1 ORM 解决什么问题如果每个功能都要自己写 SQL开发效率会很低而且容易出错。Django 内置了 ORM也就是对象关系映射它让你用 Python 类来操作数据库。一个模型类对应一张表类的属性对应表的字段。创建一条记录就是实例化一个对象查询就是调用objects上的方法。这样做的好处是你不需要关心底层是 SQLite 还是 MySQLDjango 会自动生成对应的 SQL。5.2 定义模型与数据迁移在blog/models.py里定义一个文章模型from django.db import models class Post(models.Model): title models.CharField(max_length200) content models.TextField() created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.title字段类型需要记住几个常用的字段类型用途常见参数CharField短文本max_length 必填TextField长文本无IntegerField整数default 可选DateTimeField日期时间auto_now_add、auto_nowBooleanField布尔值default 必填ForeignKey外键多对一to、on_delete定义完模型之后还要执行两步命令python manage.py makemigrations python manage.py migratemakemigrations会生成迁移文件相当于记录这次模型改动的“变更单”migrate把变更真正应用到数据库。以后每次修改模型都要重新执行这两条命令。如果做完发现数据库没变化先检查blog是否已经加进了INSTALLED_APPS。5.3 增删改查与删除对象Django 的数据库操作全部在模型对象上完成。最常见的写法# 创建 Post.objects.create(title第一篇文章, content内容) # 查询所有 all_posts Post.objects.all() # 按条件过滤 recent_posts Post.objects.filter(title__containsDjango) # 查询单条不存在会报 DoesNotExist post Post.objects.get(pk1) # 更新 post.title 修改后的标题 post.save() # 删除 post.delete()删除对象是热词里经常被搜到的操作这里多说一句delete()返回一个元组第一个数字是删除的记录条数第二个是明细。删数据是不可逆的建议在写删除功能时先加一层确认比如要求用户点击确认按钮或者把delete操作记录到日志里。查询结果也有一个常见的坑filter返回的不是单个对象而是一个查询集。即使只查到一条你也要用for循环或者.first()取出来直接赋值给变量会得到查询集而不是文章对象。6. 把主线串起来从零做一个文章列表和详情页6.1 文章列表页前面几章已经把路由、视图、模板、模型都过了一遍现在把它们串成一个完整功能。第一步在config/urls.py里把blog.urls引入这一步如果还没做先回到第三章补上。第二步写视图。列表页需要从数据库取出文章传给模板# blog/views.py from django.shortcuts import render from .models import Post def index(request): posts Post.objects.all().order_by(-created_at) return render(request, blog/index.html, {posts: posts})order_by(-created_at)表示按照创建时间倒序排列减号代表倒序。第三步写模板{% extends blog/base.html %} {% block content %} {% for post in posts %} h2 a href{% url post_detail pkpost.pk %}{{ post.title }}/a /h2 p{{ post.content|truncatechars:50 }}/p {% empty %} p还没有文章/p {% endfor %} {% endblock %}模板里的{% empty %}是查询集为空时显示的内容这个细节很实用避免页面空白让用户以为出错了。6.2 文章详情页详情页需要接收 URL 里的文章 ID。路由、视图、模板三处都要配合# blog/urls.py path(post/int:pk/, views.post_detail, namepost_detail),# blog/views.py from django.shortcuts import render, get_object_or_404 def post_detail(request, pk): post get_object_or_404(Post, pkpk) return render(request, blog/post_detail.html, {post: post})这里用了get_object_or_404它比get更好用查不到文章时直接返回 404 页面而不是让程序报错。这是 Django 设计里非常顺手的一个工具。模板{% extends blog/base.html %} {% block content %} h1{{ post.title }}/h1 p{{ post.created_at }}/p div{{ post.content|linebreaks }}/div a href{% url index %}返回列表/a {% endblock %}linebreaks过滤器会把数据库里的换行符转换成 HTML 的段落标签否则多行文本会挤成一行。这种小细节特别能提升页面体验值得记下来。6.3 表单提交数据列表和详情都有了再加一个发布文章的功能。表单是 Django 里有点门槛的部分但主线只需要掌握最基础的做法。先建一个blog/forms.py# blog/forms.py from django import forms from .models import Post class PostForm(forms.ModelForm): class Meta: model Post fields [title, content]视图里同时处理两种请求方式# blog/views.py from django.shortcuts import render, redirect from .forms import PostForm def create_post(request): if request.method POST: form PostForm(request.POST) if form.is_valid(): form.save() return redirect(index) else: form PostForm() return render(request, blog/create_post.html, {form: form})模板里渲染表单{% extends blog/base.html %} {% block content %} form methodpost {% csrf_token %} {{ form.as_p }} button typesubmit发布/button /form {% endblock %}{% csrf_token %}是 Django 的安全机制表单里必须带上否则提交会报 403 错误。这里重点说判断标准表单提交成功后应该立即redirect而不是直接render一个完成页面。原因是render会让刷新按钮重复提交表单redirect会重新发起一次 GET 请求避免数据重复写入。你可以在数据库里连续插入两条相同记录来验证这个问题是否发生。6.4 测试和验证方式功能写完后建议按下面的顺序验证启动服务访问首页确认文章列表能显示。点击一篇文章标题确认能进入详情页。打开发布页面填入内容提交确认跳回列表页且新文章出现。检查数据库里是否真的多了一条记录。可以直接在命令行执行python manage.py shell然后输入Post.objects.all()查看。尝试访问不存在的文章 ID比如post/999/确认返回 404 而不是报错。这个验证顺序覆盖了路由、视图、模板、模型、表单全部环节。哪一步失败就回到对应章节排查不要跳过任何一步直接说“功能完成了”。7. 新手最容易卡住的问题和排查顺序7.1 常见报错现象我整理了几个新手高频问题每个都有明确的指向报错现象常见原因排查方向ModuleNotFoundError: No module named blogApp 没有安装或路径不对检查INSTALLED_APPSNo matching query exists使用get查不存在的记录改用get_object_or_404或filterTemplateDoesNotExist模板路径不对或文件名拼错检查templates目录结构CSRF token missing or incorrect表单里没写{% csrf_token %}补充 csrf_tokenOperationalError: no such table模型定义了但没迁移执行 makemigrations 和 migrateDisallowedHostDEBUG 关闭但没配置 ALLOWED_HOSTS添加域名到 ALLOWED_HOSTS这里再补一个容易被忽略的问题post/int:pk/这种路由末尾必须带斜杠。如果访问post/1不带斜杠Django 默认会返回一个重定向。如果你把APPEND_SLASH设置改了不带斜杠可能直接 404。7.2 推荐的排查顺序遇到问题不要急着改代码按照从外到内的顺序排查先看现象。是报错、卡住、空白页还是数据不对。不同现象指向不同方向。再看地址。URL 输入是否正确路由规则是否匹配参数类型是否对。再看日志。Django 的开发服务器会把异常详细信息打到终端包括堆栈、出错文件、具体行号。这是最有价值的线索。再看数据。进入python manage.py shell手动执行视图里那几条查询看数据库里到底有没有数据。最后看参数。检查表单字段、模板变量名、路由参数名是否和视图函数形参一致。我见过很多人一报错就怀疑是自己的代码写得有问题结果查了半天发现是虚拟环境没有激活跑的是系统旧 Python。所以第一步先确认当前环境再谈改代码。7.3 容易被忽略的坑第一修改了models.py之后迁移失败通常是因为数据库里已经存在同名表。如果只是学习阶段可以直接删掉旧的 SQLite 数据库文件重新migrate。但正式项目里不要这么做要学习用迁移文件回滚。第二模板变量名打错了Django 在 DEBUG 模式下可能不会直接报错而是输出空字符串。这时候页面不报错但显示空白最容易让人误判成数据问题。先检查视图传的字典键名和模板里的变量名是否完全一致。第三不要一上来就装一堆 Django 扩展包。新手阶段的标准库功能足够等你能独立完成一个博客项目再考虑第三方包、认证方案和 REST 接口框架。每多一个包就多一层排错成本。第四端口冲突很常见。默认的 8000 端口如果被占用运行runserver会提示端口被占用。改用其他端口python manage.py runserver 80018. 主线之后Admin 后台、认证、DRF 与部署主线跑通后你会发现 Django 的能力远不止这些。接下来按照项目需求选学这几块。管理员后台是 Django 最出名的功能之一。在blog/admin.py里注册模型from django.contrib import admin from .models import Post admin.site.register(Post)然后创建超级用户访问http://127.0.0.1:8000/admin/就能看到管理界面可以对文章进行增删改查不用写一行前端代码。这是理解 Django“内置能力”的经典入口。用户注册和登录可以先用 Django 自带的auth应用配合LoginView和LogoutView使用。等你能做简单的登录跳转再去研究自定义用户模型不要反过来。接口开发是另一个大方向。如果你想把数据提供给小程序、App 或者其他系统可以学 Django Rest Framework它是在 Django 基础之上的接口工具。但前提是你已经理解了视图和模型的关系否则直接学 DRF 会非常吃力。部署上线方面新手最容易踩的坑是DEBUG False之后静态文件全丢了。这是正常现象生产环境必须额外配置静态文件服务。先不要纠结 Nginx 和 WSGI 的细节可以先用最简单的部署方式跑通再逐步优化。最后说一句Django 是一个非常稳的框架它在国内 Web 开发里仍然有大量真实项目使用尤其适合内容型网站、管理后台、接口服务这些场景。你不需要学完所有功能再去动手沿着这条主线把第一个项目做出来比看十遍教程都有用。遇到问题的时候记住一个原则先确认输入再看日志再动代码绝大多数问题都能在这三步里找到答案。
返回列表