Ⅰ 反向解析
【一】基础的URL配置
- 在实际的Django项目中,经常需要获取某个具体对象的URL,为生成的内容配置URL链接。
- 比如,我要在页面上展示一列文章列表,每个条目都是个超级链接,点击就进入该文章的详细页面。
- 现在我们的urlconf是这么配置的:
path('post/<int:pk>/',views.some_view),
【二】问题引入
- 在前端中,这就需要为HTML的< a>标签的href属性提供一个诸如http://www.xxx.com/post/3/的值。其中的域名部分,Django会帮你自动添加无须关心,我们关注的是post/3/。
- 此时,一定不能硬编码URL为post/3/,那样费时费力、修改困难,而且容易出错。试想,如果哪天,因为某种原因,需要将urlconf中的表达式改成entry/int:pk/,为了让链接正常工作,必须修改对应的herf属性值,于是你去项目里将所有的post/.../都改成entry/.../吗?显然这是不现实的!
- 我们需要一种安全、可靠、自适应的机制,当修改URLconf中的代码后,无需在项目源码中大范围搜索、替换失效的硬编码URL。
【1】页面上有很多a标签链接了其他路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
</body>
</html>
from django.shortcuts import render,HttpResponse
def index(request):
return render(request, 'index.html')
def func(request):
return HttpResponse('from func view')


【2】路由匹配表达式出现了变化,html页面上的地址全部失效


【三】解决方案
- 通过反向解析可以获取到一个结果,该结果可以访问到一个路由
- 为了解决这个问题,Django提供了一种解决方案,只需在URL中提供一个name参数,并赋值一个你自定义的、好记的、直观的字符串。
- 通过这个name参数,可以反向解析URL、反向URL匹配、反向URL查询或者简单的URL反查
【四】解决方案实现
- 在需要解析URL的地方,对于不同层级,Django提供了不同的工具用于URL反查:
- 在模板语言中:使用url模板标签。(也就是写前端网页时)
- 在Python代码中:使用reverse()函数。(也就是写视图函数等情况时)
- 在更高层的与处理Django模型实例相关的代码中:使用get_absolute_url()方法。(也就是在模型model中,参考前面的章节)
- 所有上面三种方式,都依赖于首先在path为url添加name属性!
【1】前端使用起别名
from django.urls import path
from user.views import index,func
urlpatterns = [
path('admin/', admin.site.urls),
path('index/',index),
path('func666/',func,name='func_view'),
- 使用{% url '路由规则名字' %}并且现在再前端页面点击也可以跳转
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="{% url 'func_view' %}">func_view</a>
<a href="{% url 'func_view' %}">func_view</a>
<a href="{% url 'func_view' %}">func_view</a>
<a href="{% url 'func_view' %}">func_view</a>
</body>
</html>


from django.contrib import admin
from django.urls import path
from user.views import index,func
urlpatterns = [
path('admin/', admin.site.urls),
path('index/',index),
path('func9999999/',func,name='func_view'),



【2】后端使用
from django.shortcuts import reverse
reverse("定义的规则名字") ---> 帮你解析到指定的路由规则
from django.shortcuts import render,HttpResponse,reverse
def index(request):
print(reverse('func_view'))
return render(to=reverse('func_view'))
def func(request):
return HttpResponse('from func view')
Ⅱ 有名分组和无名分组的反向解析
- 本质上还是通过一些方法得到一个结果,该结果可以访问到对应的url从而触发相应的视图和功能
【一】无名分组反向路由解析
【1】前端的反向解析
from django.contrib import admin
from django.urls import path,re_path
from user.views import index,func,no_name,have_name
urlpatterns = [
re_path(r'^no_name/(\d+)/(\d+)/',no_name,name='no_name'),
re_path(r'^have_name/(?P<year>\d+)/',have_name,name='have_name')
]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>旧语法</h1>
<a href="http://localhost:8000/no_name/1/">no</a>
<a href="http://localhost:8000/have_name/2/">have</a>
<h1>新语法</h1>
<a href="{% url 'no_name' 1 %}">no</a>
<a href="{% url 'have_name' '2' %}">have</a>
</body>
</html>
【2】后端的反向解析
from django.shortcuts import render, HttpResponse, reverse
def no_name(request, *args, **kwargs):
print(reverse("no_name", args=('1', '2')))
【二】有名分组的反向解析
【1】前端的反向解析
re_path(r'^have_name/(?P<year>\d+)/',have_name,name='have_name')
<p><a href="{% url 'have_name' '2' %}">you_name</a></p>
【2】后端反向解析
print(reverse("have_name", args=(1,)))
print(reverse("have_name", kwargs={"year":1}))
【三】代码展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<a href="/func/">func</a>
<h1>旧语法</h1>
<a href="http://localhost:8000/no_name/1/">no</a>
<a href="http://localhost:8000/have_name/2/">have</a>
<h1>新语法</h1>
<a href="{% url 'no_name' 1 2 %}">no</a>
<a href="{% url 'have_name' '2' %}">have</a>
</body>
</html>
from django.shortcuts import render, HttpResponse, reverse
def index(request):
return render(request, 'index.html')
def func(request):
return HttpResponse('from func view')
def no_name(request, *args, **kwargs):
print(reverse("no_name", args=('1', '2')))
print(args)
print(kwargs)
return HttpResponse('no_name')
def have_name(request, *args, **kwargs):
print(reverse('have_name',args=(1,)))
print(reverse('have_name', kwargs={'year':1}))
print(args)
print(kwargs)
return HttpResponse('have_name')
from django.contrib import admin
from django.urls import path,re_path
from user.views import index,func,no_name,have_name
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', index),
path('func/', func),
re_path(r'^no_name/(\d+)/(\d+)/',no_name,name='no_name'),
re_path(r'^have_name/(?P<year>\d+)/',have_name,name='have_name')
]
Ⅲ 路由分发
【一】前言引入
- Django每一个应用都可以拥有属于自己的
- templates文件夹
- urls.py
- static文件夹
- 正是基于上述的特点,Django可以很好的做到自己的分组开发(每个人只写自己的app)
- 最后只需要将所有的app拷贝到新的文件,并将这些APP注册到配置文件中,然后再利用路由分发的特点,将所有的APP整合起来
- 当一个项目中的URL特别多的时候,总路由urls.py的代码非常冗余而且不好维护,这个时候就可以利用路由分发来减轻总路由的压力
- 利用路由分发之后,总路由不再干预路由与视图函数的直接对应关系
- 而是做一个分发处理
- 识别当前的url是属于哪个应用下的,直接分发给对应的应用去处理
【二】路由分发系统
- 通常,我们会在每个app里,各自创建一个urls.py路由模块,然后从根路由出发,将app所属的url请求,全部转发到相应的urls.py模块中。
urlpatterns = [
path('user/login/', login),
path('user/register/', register),
path('user/logout/', logout),
path('shop/buy_good/', buy_good),
path('shop/payfor/', payfor),
]
urlpatterns = [ ]
user_urlpatterns = [
path('user/login/', login),
path('user/register/', register),
path('user/logout/', logout),
]
shop_urlpatterns = [
path('shop/buy_good/', buy_good),
path('shop/payfor/', payfor),
]
urlpatterns += user_urlpatterns
urlpatterns += shop_urlpatterns
from django.urls import include
path("user/",include("user.urls")),
path("shop/",include("shop.urls")),
urlpatterns = []
【三】路由分发后的反向解析
path("user/",include("user.urls")),
urlpatterns = [
path("user/admin/login",login,name="login")
]
【四】路由携带额外参数
path("buy/<int:year>/", buy, {"good_name": "玩偶"}, name="buy")
def buy(request,*args,**kwargs):
print(args)
print(kwargs)
return HttpResponse("buy")
http://localhost:8000/shop/buy/2/
【五】include分发携带额外参数
path("shop/", include("shop.urls"), {"tag": "shop"}),
访问 buy 的时候
iclude 分发 带了 {"tag": "shop"}
buy 自己有一个 {"good_name": "玩偶"}
Ⅳ 名称空间
【一】应用命名空间(app_name)
【1】引入
- 前面我们为介绍了path的name参数,为路由添加别名,实现反向url解析和软编码解耦。
- 但是,我们思考这么一个问题,假设下面的情况:
- appA,有一条路由A,它的name叫做'index'
- appB,有一条路由B,它的name也叫做'index'
- 这种情况完全是有可能的,甚至还常见!
- 请问,你在某个视图中使用reverse('index',args=(...))或者在模板中使用{% url 'index' ... %},它最终生成的URL到底是路由A还是路由B呢?
- 不管是哪个,肯定都不好,因为我们需要确定状态的路由,而不是混乱的。
- 之所以造成这种情况,根本上还是各个app之间没有统一的路由管理,实际上也不可能有。
【2】解决方案
- 最佳解决问题的办法就是隔离分治,命名空间解耦,这也是常用的设计模式。
- Django提供了一个叫做app_name的属性,帮我们实现应用级别的命名空间,这样,虽然大家都叫‘大伟’,但一个是‘张大伟’,一个是‘王大伟’,自然就分清楚了甲乙丙丁。
from django.urls import path
from . import views
app_name = 'your_app_name'
urlpatterns = [
...
]
【3】使用方法
- 我么只需要在app自己本身的urls.py文件内,添加一行app_name = 'your_app_name'即可。注意不是在根路由文件中。一般就和自己的app同命即可,因为项目里面不会有2个同样名字的app。
- 使用的方式很简单:
reverse('your_app_name:index',args=(...))
{% url 'your_app_name:index': ... %}
- 注意your_app_name和index之间是冒号分隔。
app_name = 'user'
path('index/', index, name="index"),
def index(request):
print(reverse("user:index"))
return HttpResponse("user 下的 index")
<p><a href="{% url 'user:index' %}">user 下 的 index页面</a></p>
app_name = 'shop'
path("index/", index, name="index"),
def index(request):
print(reverse("shop:index"))
return HttpResponse(" shop 下的 index")
<p><a href="{% url 'shop:index' %}">shop 下 的 index页面</a></p>
【二】include 分发的时候里面有一个参数 namespace
【1】引入
- 首先我们要知道,Django是可以对app进行实例化的。也就是说:
- 一个app在运行的时候,可以同时生成多个实例
- 每个实例运行同样的代码
- 但是不同的实例,可能会有不同的状态
- 以上不好理解,甚至很多人都不知道这个知识点。
- 假设我们有个app,实现一个index页面展示功能:
- 假设访问Django服务器的人分两类,author和publisher,作者和出版社
- 他们都需要访问app
- 业务需求:为两类人实现不同的权限或者页面内容
- 尽可能重用代码
- 为此,我们可以这么实现:
- 根据不同的url来区分两类人,author访问author/...,publisher访问publisher/...。
- 两个url都指向同一个app的url:include('app.urls')
- 在视图中,根据来访人员的不同,if/else判断,实现不同的业务逻辑。
- 这样,我们就相当于共用了urls和views实现了两套app
- 而这,就是所谓的app的多个实例!
【2】问题
- 但这种做法有个明显的问题,就是对于每条URL,如何区分两种人员?
- 使用应用命名?像reverse('your_app_name:index',args=(...))这样?
- 显然是不行的,因为多个应用的实例共享应用空间名,通过app_name是区分不了的!
- 针对这种情况,Django提供了一个namespace属性,用于标记不同的应用实例,如下所示:
path("shop/", include(("shop.urls", "shop"), namespace="shop")),
path("user/", include(("user.urls", "user"), namespace="user")),
Ⅴ 虚拟环境
- 在正常开发中,我们会给每一个项目独有的解释器环境
- 该环境内只有该项目用到的模块,用不到的一概不装
【一】虚拟环境
- 每创建一个虚拟环境就类似于重新下载了一个纯净的python解释器
- 但是虚拟器不建议下载太多,创建虚拟环境是需要消耗磁盘空间的
【二】模块管理文件
- 每个项目都需要用到很多模块,并且每个模块的版本可能是不一样的
- 这种情况下我们会给每一个项目配备一个requirements.txt文件,里面存放着我们这个项目所安装的所有模块及版本
- 只需要一条命令即可安装所有模块及版本
【三】模块文件导出和安装
【1】导出项目模块文件
pip freeze > requirements.txt
- 这行命令的含义是 "freeze"(冻结)当前环境中的所有已安装包及其版本信息,并将其输出重定向到 requirements.txt 文件中。
- 执行此命令后,requirements.txt 将列出你项目所需的全部依赖及其对应版本号。
【2】安装项目模块文件
pip install -r requirements.txt
- 这将按照 requirements.txt 中列出的顺序及版本要求安装相应的 Python 包。
【四】虚拟环境补充
Ⅵ 路径转换器
【一】Django路径转换器 Django2.x 开始出现的
【二】语法
path("路由规则",视图函数名,name="路由解析名")
【三】在每一个路由中都可能会进行携带参数
【四】Django 提供了五种路径转换器
- str
- 将参数转换为 str 类型
- 匹配除了 '/' 之外的非空字符串。
- 如果表达式内不包含转换器,则会默认匹配字符串。
- int
- 将参数转换为 int 类型
- 匹配 0 或任何正整数。返回一个 int 。
- slug
- 匹配任意由 ASCII 字母或数字以及连字符和下划线组成的短标签。
- 比如,building-your-1st-django-site 。
- uuid
- 匹配一个格式化的 UUID 。为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。
- 比如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID 实例。
- path
- 匹配非空字段,包括路径分隔符 '/' 。
- 它允许你匹配完整的 URL 路径而不是像 str 那样匹配 URL 的一部分。
【四】自定义转换器
class FourDigitYearConverter:
regex = r'[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value
from django.urls import register_converter
from user.path_converters import FourDigitYearConverter
register_converter(类名,type_name= '自定义转换器名字')
parse_self = register_converter(FourDigitYearConverter, 'my_path')
path("self_parse/<my_path:param>/", self_parse, name="self_parse")
def self_parse(request, param, *args, **kwargs):
print(param, type(param))
print(args)
print(kwargs)
return redirect(reverse("index_home"))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY