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() 函数做了三件事:

  1. 加载模板文件 crm/customer_list.html
  2. customers 数据注入到模板上下文中
  3. 渲染成完整的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”这种样子。常用的过滤器还有 defaultlengthslice 等,你可以在官方文档里查到完整列表。

模板最佳实践:

  • 模板里不要写复杂的业务逻辑,只做展示
  • {% include %} 抽取公共部分(如导航栏、页脚)
  • {% extends %} 实现模板继承,避免重复写HTML骨架
  • 模板标签和变量之间加空格,比如 {% for item in list %},不要写成 {%for item in list%}

4.5 串联起来:一次完整的请求之旅

现在我们把所有知识点串起来,看看一次请求是怎么走完的:

  1. 用户在浏览器输入 http://localhost:8000/crm/customers/
  2. Django的URL调度器匹配到 crm/urls.py 里的 customer_list 路由
  3. 调用 views.customer_list 视图函数
  4. 视图函数通过 Customer.objects.all() 从数据库获取数据(Model层)
  5. 视图函数调用 render(),把数据传给模板(Template层)
  6. 模板引擎渲染出完整的HTML页面
  7. 浏览器收到HTML,展示给用户

你看,MTV三个角色各司其职,配合得天衣无缝。这就是Django优雅的地方。

我的建议:刚开始学的时候,别急着用类视图(Class-Based Views)。先用函数视图(Function-Based Views)把流程跑通,理解清楚每一步在干什么。等你对MTV模式烂熟于心了,再去看类视图,会发现那不过是函数视图的“语法糖”而已。

好了,这一章的内容就到这里。下一章我们开始真正搭建CRM系统的数据库模型,到时候你会看到Model层的强大之处。