Django框架学习笔记 - include()区分app_name和namespace

前言

查看官方文档
在这里插入图片描述
这部分描述有点晦涩难懂,我们用不同的例子来比较以下二者的区别:

我的Django版本:4.2.2
项目名称:djangoProject
有子应用:note

app_name的两种写法

app_name有两种写法,一种是写在根路由,一种是写在子路由:

  1. 在根路由中定义

    # urls.py
    urlpatterns = [
        path('notes/', include(('note.urls', 'note'))),
    ]
    
    
    # note/urls.py
    urlpatterns = [
        path('namespace_test/', views.namespace_test, name='namespace_test'),
    ]
    
  2. 在子路由中定义

    # urls.py
    urlpatterns = [
        path('notes/', include(('note.urls', 'note'))),
    ]
    
    
    # note/urls.py
    app_name = "note"
    urlpatterns = [
        path('namespace_test/', views.namespace_test, name='namespace_test'),
    ]
    

app_name顾名思义应用命名空间,通常与子应用同名,因此常写在子应用 app.urls 中指定;

当同时在主路由urls和子路由app.urls中指定指定时,主路由的命名无效。

# urls.py
urlpatterns = [
    path('notes/', include(('note.urls', 'notes'))),
]


# note/urls.py
app_name = "note"
urlpatterns = [
    path('namespace_test/', views.namespace_test, name='namespace_test'),
]

在这里插入图片描述

namespace的写法

namespace在根路由中定义

# urls.py
urlpatterns = [
    path('notes/', include('note.urls', namespace='note')),
]


# note/urls.py
app_name = "note"
urlpatterns = [
	path('namespace_test/', views.namespace_test, name='namespace_test'),
]

# urls.py
urlpatterns = [
    path('notes/', include(('note.urls', 'note'), namespace='note')),
]

namespace为实例命名空间,通常在创建实例,即 path(route, include()) 函数中指定;

namespace默认值

当指定app_name,不指定namespace时:

# urls.py
urlpatterns = [
    path('notes/', include('note.urls')),
]


# note/urls.py
app_name = "note"
urlpatterns = [
	path('namespace_test/', views.namespace_test, name='namespace_test'),
]


# note/views.py
from django.http import HttpResponse
import logging

# logging日志配置可以参考我的另一篇文章:https://blog.csdn.net/qq_42694871/article/details/131528016
logger = logging.getLogger('django')


def namespace_test(request):
    """ 测试命名空间的区别 """
    logger.info("app_name: %s", request.resolver_match.app_name)
    logger.info("namespace: %s", request.resolver_match.namespace)
    logger.info("view_name: %s", request.resolver_match.view_name)
    return HttpResponse('success')

在这里插入图片描述

可以看到,namespace默认等于app_name的值。

namespace的唯一性

基于同一个子应用note (即同一个app_name) 创建多个path()实例:

# urls.py
urlpatterns = [
    # 唯一的namespace="admin_note"
    path('admin_note/', include('note.urls', namespace='admin_note')),
    # 唯一的namespace="tourist_note"
    path('tourist_note/', include('note.urls', namespace='tourist_note')),
    # 不唯一的namespace="user_note"
    path('user_test_note/', include('note.urls', namespace='user_note')),
    path('user_dev_note/', include('note.urls', namespace='user_note')),
    path('user_pro_note/', include('note.urls', namespace='user_note')),
]

编译时会生成警告:URL命名空间 user_note 不唯一
在这里插入图片描述

请求每个url均可正常访问。

说明Django不推荐使用重复的namespace,而不是禁止。
根据警告信息我们可以知道,不唯一的namespace可能导致无法reverse()其下所有的url:

# 查询namespace='user_note'的实例,返回第一个匹配的url
>>> reverse('user_note:namespace_test')
'/user_test_note/namespace_test/'
# 查询current_app='user_note'下的app_name='note'的实例,返回第一个匹配的url
>>> reverse('note:namespace_test', current_app='user_note')
'/user_test_note/namespace_test/'

# 交换urls中user_test_note/和user_pro_note/的顺序,同样返回第一个

>>> reverse('user_note:namespace_test')
'/user_pro_note/namespace_test/'
>>> reverse('note:namespace_test', current_app='user_note')
'/user_pro_note/namespace_test/'

可以看到,虽然没有报错,但是只能对第一个创建的实例下的路由进行反转。

也可能会报错:NoReverseMatch: ‘xxx’ is not a registered namespace

对其他唯一的namespace:

# 查询namespace='admin_note'的实例
>>> reverse('admin_note:namespace_test')
'/admin_note/namespace_test/'
# 查询namespace='tourist_note'的实例
>>> reverse('tourist_note:namespace_test')
'/tourist_note/namespace_test/'
# 查询current_app='admin_note'下app_name='note'的实例
>>> reverse('note:namespace_test', current_app='admin_note')
'/admin_note/namespace_test/'
# 查询current_app='tourist_note'下app_name='note'的实例
>>> reverse('note:namespace_test', current_app='tourist_note')
'/tourist_note/namespace_test/'
# 查询不存在的current_app下app_name='note'的实例,返回最后一个url,但因为最后三个namespace重复,所以返回倒数第三个url
>>> reverse('note:namespace_test', current_app='noteapp')
'/user_test_note/namespace_test/'

可以看到,当namespace唯一时,reverse()函数可以成功解析。

app_name的唯一性

从上述示例可以看出,同一个子应用的app_name可以不唯一,即无论有多少实例,应用app_name始终为同一个。

  1. 同一个应用使用不同的app_name:

    # urls.py
    urlpatterns = [
        path('admin_note/', include(('note.urls', 'adminNote'), namespace='admin_note')),
        path('tourist_note/', include(('note.urls', 'touristNote'), namespace='tourist_note')),
    ]
    
    
    # note/urls.py
    urlpatterns = [
        path('namespace_test/', views.namespace_test, name='namespace_test'),
    ]
    

    编译与运行无异常,reverse() 成功:

    >>> reverse('admin_note:namespace_test')
    '/admin_note/namespace_test/'
    >>> reverse('tourist_note:namespace_test')
    '/tourist_note/namespace_test/'
    >>> reverse('tourist_note:namespace_test')
    '/tourist_note/namespace_test/'
    >>> reverse('adminNote:namespace_test')
    '/admin_note/namespace_test/'
    >>> reverse('touristNote:namespace_test')
    '/tourist_note/namespace_test/'
    
  2. 不同的应用使用同一个app_name:

    # urls.py
    urlpatterns = [
        path('notes/', include('note.urls', namespace='note')),
        path('plans/', include('plan.urls', namespace='plan')),
    ]
    
    
    # note/urls.py
    app_name = "app"
    urlpatterns = [
        path('namespace_test/', views.namespace_test, name='namespace_test'),
    ]
    
    
    # plan/urls.py
    app_name = "app"
    urlpatterns = [
        path('namespace_test/', views.namespace_test, name='namespace_test'),
    ]
    

    编译与运行无异常,reverse() 成功:

    >>> reverse('note:namespace_test')
    '/notes/namespace_test/'
    >>> reverse('plan:namespace_test')
    '/plans/namespace_test/'
    >>> reverse('app:namespace_test')
    '/plans/namespace_test/'
    >>> reverse('app:namespace_test', current_app='note')
    '/notes/namespace_test/'
    >>> reverse('app:namespace_test', current_app='plan')
    '/plans/namespace_test/'
    

结论

  1. app_name为应用命名空间,通常写在子应用的urls.py中:app_name = "app_name",与子应用同名。
  2. app_name的唯一性没有要求,通常为每个子应用设定各自的app_name。
    • 同一个子应用即可以使用同一个app_name,也可以使用不同的app_name;
    • 不同的子应用,同样既可以使用同一个app_name,也可以使用不同的app_name;
  3. namespace为实例命名空间,通常写在主路由的urls.py中:path(route, include(viewname, "namespace")).
  4. namespace的值应该是唯一的,否则可能无法反转这个命名空间下的所有url。
  5. 当没有指定namespace的值时,它默认等于app_name的值。因此当同一个app_name有不同实例时,需要注意设定namespace唯一。

按照代码规范写法,app_name相当于子应用的实际名字,而namespace相当于子应用在某个实例上的昵称。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐