reverse() 是把视图名变回 URL 的那半边。它有哪些行为你以为知道、其实没验证过:参数类型怎么校验、同名怎么选、命名空间怎么解析、第一次调用有多贵?这篇逐条实测。
reverse('post', args=[5]) → /post/5/
reverse('post', kwargs={'pk': 5}) → /post/5/
reverse('post', args=['5']) → /post/5/
reverse('post', args=['abc']) → NoReverseMatch: Reverse for 'post' with arguments '('abc',)' not found. 1 pattern(s) tried: ['post/(?P<pk>[0-9]+)/\\Z']
reverse('post') → NoReverseMatch: Reverse for 'post' with no arguments not found. 1 pattern(s) tried: ['post/(?P<pk>[0-9]+)/\\Z']
reverse('post', args=[5, 6]) → NoReverseMatch: Reverse for 'post' with arguments '(5, 6)' not found. 1 pattern(s) tried: ['post/(?P<pk>[0-9]+)/\\Z']
reverse('post', kwargs={'pk': 5, 'x': 1}) → NoReverseMatch: Reverse for 'post' with keyword arguments '{'pk': 5, 'x': 1}' not found. 1 pattern(s) tried: ['post/(?P<pk>[0-9]+)/\\Z']
reverse('post-edit', kwargs={'slug': 'a b/é'}) → NoReverseMatch: Reverse for 'post-edit' with keyword arguments '{'slug': 'a b/é'}' not found. 1 pattern(s) tried: ['post/(?P<slug>[-a-zA-Z0-9_]+)/edit/\\Z']
reverse('post-edit', kwargs={'slug': 'ok-slug_1'}) → /post/ok-slug_1/edit/
reverse('arch', kwargs={'year': 7}) → /arch/0007/
reverse('arch', kwargs={'year': 2026}) → /arch/2026/
reverse('files', kwargs={'p': 'a b/c d.txt'}) → /files/a%20b/c%20d.txt
reverse('re', args=['1', '2']) → /re/1/2/
reverse('re', kwargs={'a': 1, 'b': 2}) → /re/1/2/
reverse('nope') → NoReverseMatch: Reverse for 'nope' not found. 'nope' is not a valid view function or pattern name.报错信息里有「已尝试的模式」和你传入的参数,基本足够定位。注意 args=['5'](字符串)也能反向出 /post/5/——因为反向时是用模式的正则去匹配参数的字符串形式。
opt(无参数) → /opt/
opt(page=3) → /opt/3/
dup(两个同名同参数) → /dup/two/ (定义顺序: dup/one/ 在前, dup/two/ 在后)
v1:users → /api/users/
v2:user → /api2/users/3/
reverse('api:users', current_app='v2') → /api2/users/同一个 name 出现多次是允许的:Django 会按你传入的参数找能匹配的那条(opt 的两个模式)。如果两条都能匹配,后定义的胜出——所以给不同页面起同一个 name 是个静悄悄的 bug。
N=10 第一次 reverse 1.08 ms 之后每次 7.56 µs N=1000 第一次 reverse 27.08 ms 之后每次 7.64 µs N=5000 第一次 reverse 139.83 ms 之后每次 7.75 µs
第一次 reverse() 会构建整个 urlconf 的反向字典(与路由数量成正比),之后每次调用几乎是常数。这也是 Django 进程「第一个请求慢一点」的来源之一。