4、Django基础与项目骨架:MTV架构理解、URL路由配置、视图函数编写、模板引擎入门
好,咱们正式开始动手写代码了。
这一章,我带你搭起Django项目的骨架。说白了,就是先搞清楚Django这套框架到底是怎么运转的。很多新手一上来就急着写业务逻辑,结果路由乱成一锅粥,视图和模板搅在一起,最后项目根本没法维护。我早期带团队时就吃过这个亏,后来老老实实把骨架搭稳了,后面开发速度反而快了好几倍。
4.1 理解MTV架构:Django的“分而治之”哲学
Django的核心思想,就是MTV模式。你可能会问,MVC我听过,MTV是什么?
其实一回事。MVC是Model-View-Controller,Django把View换成了Template,把Controller换成了View。别被名字绕晕了,我们直接看对应关系:
| MTV组件 | 对应文件/位置 | 职责 |
|---|---|---|
| Model(模型) | models.py | 操作数据库,定义数据结构 |
| Template(模板) | templates/目录 | 展示数据,生成HTML页面 |
| View(视图) | views.py | 处理业务逻辑,连接Model和Template |
我个人习惯把MTV理解成“餐厅运作模式”:
- Model 就是后厨的食材库和菜谱。它知道数据长什么样,怎么存怎么取。
- View 是厨师。他接到订单(用户请求),从后厨拿食材(Model),然后烹饪加工(业务逻辑)。
- Template 是摆盘和上菜。厨师把做好的菜交给服务员,服务员用漂亮的盘子装好端给客人。
你想想看,如果厨师又管做菜又管摆盘又管采购,那厨房肯定乱套。Django这么设计,就是为了让每个部分各司其职。
核心原则:Model只关心数据,Template只关心展示,View只关心逻辑。千万别混在一起。
4.2 URL路由配置:请求的“导航系统”
用户访问你的网站,比如 http://localhost:8000/customers/,Django怎么知道该执行哪段代码?
答案就在 urls.py 里。它就是整个应用的导航地图。
我们先看项目级的 urls.py(通常在项目根目录下):
# project/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('crm/', include('crm.urls')), # 把/crm/开头的请求,交给crm应用处理
]
这里有个关键点:include() 函数。它就像高速公路的匝道,把不同方向的流量引导到不同的应用里去。我建议你养成这个习惯——每个应用都有自己的 urls.py,然后在项目级路由里用 include 统一管理。这样项目大了之后,路由不会变成一团乱麻。
接下来是应用级的 urls.py(在 crm 应用目录下):
# crm/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('customers/', views.customer_list, name='customer_list'),
path('customers/<int:pk>/', views.customer_detail, name='customer_detail'),
]
注意看第三行:<int:pk> 这种写法叫“路径参数”。它会从URL里捕获一个整数,然后作为参数传给视图函数。比如用户访问 /crm/customers/3/,Django就会自动把 pk=3 传给 customer_detail 视图。
小技巧:给每个路由都加上 name 参数。这样在模板里用 {% url 'customer_list' %} 来生成链接,就算以后改了URL路径,模板里的链接也会自动更新。我曾经在一个项目里没加name,结果改了一次路由,找了半天才把所有硬编码的链接改完……
4.3 视图函数编写:处理请求,返回响应
视图函数,就是MTV里的“厨师”。它接收一个 HttpRequest 对象,然后返回一个 HttpResponse 对象。
最简单的视图长这样:
# crm/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("欢迎来到客户关系管理系统!")
但实际项目中,我们通常需要从数据库拿数据,然后渲染到模板里。来看一个正经的视图:
# crm/views.py
from django.shortcuts import render
from .models import Customer
def customer_list(request):
# 从数据库获取所有客户
customers = Customer.objects.all()
# 把数据传给模板
return render(request, 'crm/customer_list.html', {'customers': customers})
这里 render() 函数做了三件事:
- 加载模板文件
crm/customer_list.html - 把
customers数据注入到模板上下文中 - 渲染成完整的HTML字符串,返回给浏览器
嗯,这里要注意:视图函数里尽量不要写复杂的SQL查询。把查询逻辑封装到Model的Manager方法里,或者用 get_queryset() 来管理。我见过有人直接在视图里写 Customer.objects.raw('SELECT ...'),那画面太美我不敢看。
避坑指南:我曾经在视图里直接修改了 request 对象,结果导致后续中间件报错。记住:request 是只读的,不要试图修改它。如果需要传递额外数据,用 request.session 或者自己定义中间件。
4.4 模板引擎入门:把数据变成漂亮的页面
Django的模板引擎,说白了就是一套在HTML里嵌入动态数据的语法。它用 {{ }} 输出变量,用 {% %} 执行逻辑。
来看一个客户列表的模板:
<!-- crm/templates/crm/customer_list.html -->
<!DOCTYPE html>
<html>
<head>
<title>客户列表</title>
</head>
<body>
<h1>客户列表</h1>
<table>
<thead>
<tr>
<th>姓名</th>
<th>电话</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
{% for customer in customers %}
<tr>
<td>{{ customer.name }}</td>
<td>{{ customer.phone }}</td>
<td>{{ customer.email }}</td>
</tr>
{% empty %}
<tr>
<td colspan="3">暂无客户数据</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
注意 {% empty %} 这个标签。当 customers 列表为空时,它会自动显示“暂无客户数据”。这个细节我特别喜欢,它让模板自己处理了边界情况,视图函数就不用再写 if not customers 这种判断了。
模板里还能用过滤器,比如 {{ customer.created_at|date:"Y-m-d" }} 可以把日期格式化成“2024-01-15”这种样子。常用的过滤器还有 default、length、slice 等,你可以在官方文档里查到完整列表。
模板最佳实践:
- 模板里不要写复杂的业务逻辑,只做展示
- 用
{% include %}抽取公共部分(如导航栏、页脚) - 用
{% extends %}实现模板继承,避免重复写HTML骨架 - 模板标签和变量之间加空格,比如
{% for item in list %},不要写成{%for item in list%}
4.5 串联起来:一次完整的请求之旅
现在我们把所有知识点串起来,看看一次请求是怎么走完的:
- 用户在浏览器输入
http://localhost:8000/crm/customers/ - Django的URL调度器匹配到
crm/urls.py里的customer_list路由 - 调用
views.customer_list视图函数 - 视图函数通过
Customer.objects.all()从数据库获取数据(Model层) - 视图函数调用
render(),把数据传给模板(Template层) - 模板引擎渲染出完整的HTML页面
- 浏览器收到HTML,展示给用户
你看,MTV三个角色各司其职,配合得天衣无缝。这就是Django优雅的地方。
我的建议:刚开始学的时候,别急着用类视图(Class-Based Views)。先用函数视图(Function-Based Views)把流程跑通,理解清楚每一步在干什么。等你对MTV模式烂熟于心了,再去看类视图,会发现那不过是函数视图的“语法糖”而已。
好了,这一章的内容就到这里。下一章我们开始真正搭建CRM系统的数据库模型,到时候你会看到Model层的强大之处。