白日依山尽,黄河入海流。欲穷千里目,更上一层楼。 -- 唐·王之涣

django中URL的命名空间总结(include/namespace/app_name)

前言介绍

一般在Django中有以下两种场景:

  • 项目工程只有一个应用,但是有个特殊场景,比如学生管理应用,需要给男生和女生配置不同的URL前缀
  • 项目工程会有多个应用,每个应用中可能存在相同的URL别名,比如都会有 name=index 首页

场景二在Django中比较常见,在template模板是进行URL解析的时候,需要区分不同的应用的,所以需要定义不同的app_name

app_name

目的:区分不同应用的URL地址

案例,项目工程中存在两个应用 ,blog 和 todo ,项目结构

1
2
3
4
5
6
7
# 创建工程 
django-admin startproject django_namespace_demo

# 创建应用
cd django_namespace_demo
python manage.py startapp blog
python manage.py startapp todo

因为是作为演示,但是没有具体model模型,只需要定义view视图函数和对应的URL地址、模板文件(进行URL解析演示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# blog
# ------------------------- views.py ------------------------- #
from django.shortcuts import render
from django.http import HttpResponse

def index(request):
return render(request, 'blog/index.html')


def detail(request):
return HttpResponse("BLog Detail")

# ------------------------- urls.py ------------------------- #
from django.urls import path
from blog import views

urlpatterns = [
path('', views.index, name='index'),
path('detail/', views.detail, name='detail'),
]

# ------------------------- templates/blog/index.html ------------------------- #
Blog Index
<a href="{% url 'detail' %}">Visit Detail</a>

Todo 的结构和内容与Blog很相似,这里不做代码展示

配置入口URL和 启动服务

1
2
3
4
5
6
7
8
9
10
# django_namespace_demo/urls.py
from django.urls import path, include

urlpatterns = [
path('blog/', include('blog.urls')),
path('todo/', include('todo.urls')),
]

# 启动服务
python manage.py runserver 127.0.0.1:8007

1、访问blog首页,正常显示(这个时候是从入口URL正常进入,有path前缀 )

image-20221019102825350

2、访问detail, 访问之后发现跳转错误到了 todo detail 而不是 blog detail

image-20221019102910297

所以需要指定应用空间 来说明具体跳转到那个应用的detail

3、修改URL代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# blog/urls.py
... ..
# 这里是添加,其他不变的代码进行省略
app_name = 'blog'
urlpatterns = [
... ...
]

# todo/urls.py
... ..
# 这里是添加,其他不变的代码进行省略
app_name = 'todo'
urlpatterns = [
... ...
]

4、修改template模板代码 (📢: detail前面都添加了 app_name)

1
2
3
4
5
<!-- blog/templates/blog/index.html -->
Blog Index <a href="{% url 'blog:detail' %}">Visit Detail</a>

<!-- todo/templates/todo/index.html -->
Todo Index <a href="{% url 'todo:detail' %}">Visit Detail</a>

5、然后重新访问 blog首页(http://127.0.0.1:8007/blog/) 或者 todo 首页 (http://127.0.0.1:8007/todo/)之后点击页面的 Visit Detail 超链接,发现跳转正常。

  • blog的visit detail跳转到了 blog 的detail页面;

  • Todo的visit detail跳转到了 todo 的detail页面;

📢 这个时候是没有用到 namespace 配置项的哦~

namespace 项目级别的命令空间

目的: 区分同一个应用中的不同URL前缀

这里用学生管理应用作为案例介绍

1
2
3
# 创建应用
cd django_namespace_demo
python manage.py startapp student

视图函数

1
2
3
4
5
6
7
8
9
10
11
12
13
# student/views.py
from django.http import HttpResponse
from django.shortcuts import redirect,reverse

def index(request):
if request.GET.get("username"):
return HttpResponse("Student List Page")
else:
return redirect(reverse("login"))

def login(request):
url_path = request.path
return HttpResponse(f"Login Page and You need to Login first! (当前请求路径为: {url_path})")

Urls 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# student/urls.py
from django.urls import path
from student import views

urlpatterns = [
path('', views.index, name='index'),
path('login', views.login, name='login'),
]

# django_namespace_demo/urls.py
... ...
urlpatterns = [
... ...
path('male/', include('student.urls')),
path('female/', include('student.urls')),
]

1、访问 http://127.0.0.1:8007/male/ 希望的结果是跳转之后应该是http://127.0.0.1:8007/male/login

但是从下图知道,跳转之后的路径有问题,不符合预期

image-20221019110035185

2、修改urls

📢 因为是男女使用不同的前缀,那么我们知道只配置app_name 是没有作用的,因为app_name 对student应用而言只是一个值,而不是两个

修改项目入口的urls.py

1
2
3
4
5
6
7
# django_namespace_demo/urls.py
... ...
urlpatterns = [
... ...
path('male/', include('student.urls', namespace="male")),
path('female/', include('student.urls', namespace="female")),
]

这个时候在后台其实报错了,错误如下

1
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.

提示需要在包含的模块中配置 app_name 属性,或者需要在传递一个二元组(包含patterns和app_name)才可以

第一种方式,在student的urls.py 配置 app_name

1
2
3
4
# student/urls.py
... ...
app_name = 'student'
... ...

第二种方式,在项目入口的urls.py 中修改现有include配置

1
2
3
4
5
6
7
# django_namespace_demo/urls.py
... ...
urlpatterns = [
... ...
path('male/', include(('student.urls', 'student'), namespace="male")),
path('female/', include(('student.urls', 'student'), namespace="female")),
]

同时也需要修改视图函数中 reverse的解析

1
2
3
4
5
6
7
8
9
10
# student/views.py
def index(request):
# 添加 获取当前namespace (male or female)
current_namespace = request.resolver_match.namespace
print(f"current_namespace: {current_namespace}")
if request.GET.get("username"):
return HttpResponse("Student List Page")
else:
# 这里的命令空间前缀就是 项目入口中定义的 namespace 而不是应用中的 app_name
return redirect(reverse(f"{current_namespace}:login"))

3、然后访问 http://127.0.0.1:8007/male/ 获取期望的结果

image-20221019112227215

知识点

1、URL解析存在两个地方

​ 一个是视图函数中,一般是 reverseredirect结合使用; 用法为 return redirect(reverse("appname_or_namespace:url_aliasname"))

​ 一个是template模板文件中,一般是{% url 'appname_or_namespace:url_aliasname'%}

2、应用视图中获取项目入口配置的namespace的方法

1
current_namespace = request.resolver_match.namespace  

3、URL中include 的用法

1
2
3
4
5
6
7
8
# 常规用法
path('blog/', include('blog.urls'))

# 把app_name配置提前到 include,注意这个时候 第一个参数就是一个 二元组
path('blog/', include(('blog.urls', 'blog')))

# 配置 namespace 使用
path('male/', include(('student.urls', 'student'), namespace="male")),

4、如果视图中使用 render 利用 template解析页面的时候,需要在 INSTALLED_APPS 把应用添加上,不然提示找不到对应的 模板

当然这里有个前提是,我们使用的是TEMPALTES 中的 'APP_DIRS': True, 默认值

5、关于URL的解析reverse和 get_absolute_url 等知识点参考 django中URL反向解析总结(url/reverse/get_absolute_url)

Demo源码下载 https://gitee.com/colin5063/django-learnning-examples/tree/master/django_namespace_demo


如果觉得文章对你有用,请不吝点赞和关注公众号搜索 全栈运维 或者 DailyJobOps

个人博客 http://blog.colinspace.com/

知乎平台 https://www.zhihu.com/people/colin-31-49

CSDN平台 https://blog.csdn.net/eagle5063

简书平台 https://www.jianshu.com/u/6d793fbacc88

django中URL的命名空间总结(include/namespace/app_name)

http://blog.colinspace.com/2022/10/19/django中URL的命名空间总结-namespace-app-name/

作者

Colin

发布于

2022-10-19

许可协议