Django应用命名空间与实例命名空间
一、为什么需要url命名?
因为url是经常变化的。如果在代码中写死可能会经常改代码。给url取个名字,以后使用url的时候就使用他的名字进行反转就可以了,就不需要写死url了。
需求:访问app主页,如果没登录则自动跳转至登录页面,已经登录则留在app主页。
项目下创建两个app:
django-admin startapp app01
django-admin startapp app02
项目的settings.py
中添加这两个应用:
INSTALLED_APPS = [
...
'apps.app01',
'apps.app02'
]
项目的urls.py
中添加URL映射:
from django.urls import path, include
urlpatterns = [
...
path('app01/', include('apps.app01.urls')),
path('app02/', include('apps.app02.urls')),
]
app01和app02分别新建urls.py
并配置:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index),
path('signin/', views.login),
]
app01和app02的views.py
中:
# 以下是app01/views.py
from django.http import HttpResponse
from django.shortcuts import redirect
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse('app01首页')
else:
return redirect('./login/')
def login(request):
return HttpResponse('app01登录页面')
# 以下是app02/views.py
from django.http import HttpResponse
from django.shortcuts import redirect
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse('app02首页')
else:
return redirect('./login/')
def login(request):
return HttpResponse('app02登录页面')
启动服务:
python manage.py runserver
访问 http://127.0.0.1:8000/app01?username='onefine'
访问 http://127.0.0.1:8000/app02?username='onefine'
访问 http://127.0.0.1:8000/app01/
访问 http://127.0.0.1:8000/app02/
一切正常!
现在有了新的需求:登录时的url由login变为signin。在目前的项目中,更改需要涉及四个文件…额…
现在采用url命名来防止这种需求。
二、如何给一个url指定名称?
在path
函数中,传递一个name
参数就可以指定。
path('', views.index, name='index')
app01和app02下的urls.py
配置:
urlpatterns = [
path('', views.index, name="index"),
path('signin/', views.login, name="login"),
]
app01和app02的views.py
中:
# 以下是app01/views.py
from django.http import HttpResponse
from django.shortcuts import redirect, reverse
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse('app01首页')
else:
# url反转,url重定向
return redirect(reverse('login'))
def login(request):
return HttpResponse('app01登录页面')
# 以下是app02/views.py
from django.http import HttpResponse
from django.shortcuts import redirect, reverse
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse('app02首页')
else:
return redirect(reverse('login'))
def login(request):
return HttpResponse('app02登录页面')
重新启动服务器,输入 http://127.0.0.1:8000/app02
输入 http://127.0.0.1:8000/app01
你没看错,它确实跳转的是app2下面的signin,这是怎么回事呢?
三、应用(app)命名空间:
在多个app之间,有可能产生同名的url。这时候为了避免反转url的时候产生混淆,可以使用应用命名空间,来做区分。定义应用命名空间非常简单,只要在app
的urls.py
中定义一个叫做app_name
的变量,来指定这个应用的命名空间即可。
app01下的urls.py
配置,app02类似:
from django.urls import path
from . import views
# 应用命名空间
app_name = 'app01'
urlpatterns = [
path('', views.index, name="index"),
path('signin/', views.login, name="login"),
]
以后在做反转的时候就可以使用应用命名空间:url名称
的方式进行反转。
app01的views.py
中,app02类似:
from django.http import HttpResponse
from django.shortcuts import redirect, reverse
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse('app01首页')
else:
return redirect(reverse('app01:login'))
def login(request):
return HttpResponse('app01登录页面')
好了,完美贴合需求。
四、实例命名空间:
一个app可以创建多个实例。可以使用多个url映射到同一个app。这就会产生一个问题:以后在做反转的时候,如果使用应用命名空间,那么就会发生混淆。
项目下urls.py
文件:
urlpatterns = [
path('app01/', include('apps.app01.urls')),
path('app02/', include('apps.app02.urls')),
# 新添加的
path('app03/', include('apps.app01.urls')),
]
重启服务访问 http://127.0.0.1:8000/app03
还真是脆弱。。。
为了避免这个问题。我们可以使用实例命名空间。实例命名空间只要在include
函数中传递一个namespace
变量即可。
项目下urls.py
文件:
urlpatterns = [
url(r'^admin/', admin.site.urls),
# re_path(r'^$', views.index),
path('app01/', include('apps.app01.urls', namespace='app01')),
path('app02/', include('apps.app02.urls', namespace='app02')),
# 同一个app下的第二个实例,实例命名空间
path('app03/', include('apps.app01.urls'), namespace='app03'),
]
以后在做反转的时候,就可以根据实例命名空间来指定具体的url。
app01下的views.py
,app02类似:
from django.http import HttpResponse
from django.shortcuts import redirect, reverse
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse('app01首页')
else:
# 获取当前实例的命名空间
current_namespace = request.resolver_match.namespace
return redirect(reverse('%s:login' % current_namespace))
def login(request):
return HttpResponse('app01登录页面')
输入http://127.0.0.1:8000/app03/
输入 http://127.0.0.1:8000/app03?username='onefine'
欣慰!
注意:在使用实例命名空间之前,必须先指定一个应用命名空间——在子url.py中添加app_name变量。
'Specifying a namespace in include() without providing an app_name ’
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.