视图与URL配置:视图函数、URL路由配置、正则路由、命名空间、反向解析
好,咱们今天聊聊Django里最基础、也最核心的一块——视图和URL配置。说白了,这就是用户请求进来之后,系统怎么找到对应的代码去处理,再返回结果的过程。我刚开始学Django的时候,觉得这块就是配个路径嘛,有啥难的?后来真做项目了才发现,这里面的门道还真不少。
视图函数:你的业务逻辑入口
视图函数,就是处理请求、返回响应的那一段Python代码。它接收一个HttpRequest对象,返回一个HttpResponse对象。就这么简单。
from django.http import HttpResponse
def hello_world(request):
return HttpResponse("你好,世界!")
嗯,这里要注意:视图函数必须放在views.py文件里,这是Django的约定。我个人习惯把相关的视图放在一个文件里,比如用户相关的放user_views.py,订单相关的放order_views.py。项目大了你就知道,一个views.py里塞几百行代码,找起来真要命。
JsonResponse、redirect、render这些都很常用。
URL路由配置:请求的导航地图
URL配置,就是告诉Django:当用户访问某个网址时,该调用哪个视图函数。配置在urls.py文件里。
from django.urls import path
from . import views
urlpatterns = [
path('hello/', views.hello_world),
]
你看,path()函数接收两个参数:第一个是URL路径,第二个是对应的视图函数。用户访问/hello/时,就会执行hello_world这个函数。
我在项目中遇到过一个问题:有个同事把URL路径写成了hello(没有斜杠),结果用户访问时老是404。后来发现,Django默认会在URL末尾加斜杠,如果你没写,它就会重定向。所以,我建议你统一在路径末尾加斜杠,省得麻烦。
正则路由:更灵活的URL匹配
有时候,URL里需要包含动态参数,比如文章ID、用户名之类的。这时候就要用正则路由了。Django提供了re_path()函数,支持正则表达式。
from django.urls import re_path
from . import views
urlpatterns = [
re_path(r'^article/(?P<article_id>\d+)/$', views.article_detail),
]
这个例子中,(?P<article_id>\d+)会匹配一个或多个数字,并把匹配到的值作为article_id参数传给视图函数。视图函数里这样接收:
def article_detail(request, article_id):
return HttpResponse(f"文章ID是:{article_id}")
^或$,导致URL匹配范围过大,把不该匹配的路径也匹配上了。调试了半天才发现。所以,写正则路由时,一定要用^和$限定开头和结尾。
你想想看,如果不用正则路由,你要怎么处理动态参数?难道写几十个path()?那也太傻了。正则路由就是干这个的。
命名空间:避免URL冲突的利器
项目大了,应用多了,URL名字难免会重复。比如两个应用都有index这个URL名字,Django就懵了:到底该用哪个?这时候就需要命名空间。
命名空间分两种:app_name和namespace。前者在应用的urls.py里定义,后者在项目的urls.py里定义。
# blog/urls.py
app_name = 'blog'
urlpatterns = [
path('', views.index, name='index'),
]
# project/urls.py
urlpatterns = [
path('blog/', include('blog.urls', namespace='blog')),
]
这样,你就可以用blog:index来唯一标识这个URL了。即使其他应用也有index,也不会冲突。
我个人习惯给每个应用都加上命名空间,哪怕现在只有一个应用。为什么?因为项目总会扩展的,到时候再加命名空间,改起来就麻烦了。这叫「防患于未然」。
反向解析:别写死URL
反向解析,就是根据URL的名字和参数,动态生成URL。说白了,就是别在代码里写死/blog/123/这样的路径,而是用reverse()函数或模板标签{% url %}来生成。
from django.urls import reverse
url = reverse('blog:article_detail', args=[123])
# 结果是:/blog/123/
在模板里这样用:
<a href="{% url 'blog:article_detail' article.id %}">查看详情</a>
为什么要这么做?因为URL可能会变。比如你把/blog/改成了/articles/,如果代码里写死了路径,你就得把所有地方都改一遍。用反向解析,只需要改urls.py里的路径,其他地方自动生效。
我曾经接手过一个老项目,里面到处都是硬编码的URL。后来要改域名路径,我花了整整两天才把所有地方改完,还漏了几个,导致线上出了bug。从那以后,我再也不敢写死URL了。
综合示例:一个完整的视图与URL配置
来,咱们看一个完整的例子。假设有个博客应用,需要显示文章列表和文章详情。
# blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Article
def article_list(request):
articles = Article.objects.all()
return render(request, 'blog/article_list.html', {'articles': articles})
def article_detail(request, article_id):
article = get_object_or_404(Article, id=article_id)
return render(request, 'blog/article_detail.html', {'article': article})
# blog/urls.py
app_name = 'blog'
urlpatterns = [
path('', views.article_list, name='article_list'),
re_path(r'^article/(?P<article_id>\d+)/$', views.article_detail, name='article_detail'),
]
# project/urls.py
from django.urls import include, path
urlpatterns = [
path('blog/', include('blog.urls', namespace='blog')),
]
你看,这样配置之后,用户访问/blog/看到文章列表,访问/blog/article/1/看到第一篇文章的详情。而且,所有URL都是通过反向解析生成的,改起来很方便。
| 功能 | URL模式 | 视图函数 | URL名称 |
|---|---|---|---|
| 文章列表 | /blog/ |
article_list |
blog:article_list |
| 文章详情 | /blog/article/<id>/ |
article_detail |
blog:article_detail |
嗯,到这里,视图与URL配置的核心内容就讲完了。说白了,就是三件事:写视图函数、配URL路径、用反向解析。你想想看,是不是这个理?
最后提醒一句:别小看这些基础。我见过太多人,框架用得很溜,但URL配置乱七八糟,最后项目维护起来痛不欲生。基础打牢了,后面才能走得远。