Django笔记:URL映射
本文主要记了一些Django中URL映射相关的知识点,包括URL映射关系配置、URL传参、URL反转等,本文为学习笔记,仅供参考。
一、URL映射关系配置
URL映射关系默认是配置在主app下urls.py
中的urlpatterns
列表中,如果想要自己指定配置映射关系所在文件,只需要修改主app中settings.py
文件中的ROOT_URLCONF
配置项即可,但是urlpatterns
这个变量是固定的,无法修改,所以修改ROOT_URLCONF
配置项之后,Django就会到你指定的文件中查找urlpatterns
变量,并根据这个列表中的映射关系来进行URL的映射。urlpatterns
列表中的元素是一个经过path
或re_path
函数包装后的结果,这两个函数都在django.urls
中,直接导入使用即可。这两个函数的用法都是相似的,不同之处在于,re_path
的route
参数(即URL)需要使用正则表达式,如果对URL没有特殊要求的话,建议尽量使用path
就可以了。path
函数常见用法有两种:
- 一种是直接传入URL字符串和对应视图函数两个参数即可,例如
path("article/page/2/", views.page_detail)
,第一个route
参数为URL字符串,第二个view
参数为URL对应的视图函数。通常情况下两个参数已经够用了,但是如果想要给这个URL命令,以便用于URL的反转,可以指定name
参数。 - 另一种是指定子app的URL前缀以及使用
include
函数包装的子appurls.py
路径,例如path("articel/", include("article.urls"))
,第一个route
参数只需要指定子app的URL前缀,不需要给出完整的URL字符串,第二个view
参数为通过django.urls.include
包装后的子appurls.py
路径。这种用法涉及到URL的分层管理,可以参考后面“URL分层管理”小节。
二、URL传参
1、URL查询字符串
URL中查询字符串的参数直接使用视图函数的第一个参数request.GET
进行获取即可,查询字符串通常是通过GET
方式发送的请求,所以查询字符串的信息可以在request.GET
中得到,而这个变量是一个类似字典的数据结构,直接使用它的get
方法进行获取即可。
示例:访问http://127.0.0.1:8000/book/author/?id=3
,对应的视图函数如下
def author_detail(request):
author_id = request.GET.get("id")
text = "作者的ID是:{}".format(author_id)
return HttpResponse(text)
2、URL参数传递
如果想要URL字符串中的某部分变成参数传递给视图函数,可以在指定URL字符串时使用尖括号<param_name>
将对应参数括起来,并在视图函数中定义相同名称的参数即可。当然,也可以在URL字符串中定义多个参数,只需要在视图函数中按顺序定义同样的参数来接收即可。
注:可以给视图函数的参数指定默认值,访问时如果URL中没有传入该参数则会使用该参数的默认值。
"""视图函数代码"""
from django.http import HttpResponse
def book(request):
return HttpResponse("图书首页!")
# 注意此处的book_id必须与URL中的参数<book_id>是相同的
def book_detail(request, book_id):
text = "你获取的图书id是:{}".format(book_id)
return HttpResponse(text)
"""URL映射关系配置代码"""
urlpatterns = [
path("admin/", admin.site.urls),
path("book/", book),
path("book/detail/<book_id>/", book_detail)
]
注:使用re_path
时,传参使用正则表达式的分组命名方式,即(?P<param_name>)
,这个参数名与视图函数的参数名也是一一对应的。
3、使用URL类型转换器指定参数类型
URL字符串中传递给视图函数的参数默认都是字符串类型的,即使用的是默认的URL类型转换器str
,如果想要给参数指定为别的类型,如int
类型,则需要使用形如<int:book_id>
来进行参数类型指定。注意,如果指定了URL类型,那么在视图函数中拿到的该参数也是对应的数据类型,例如指定了参数为int
类型,不仅URL字符串中该部分必须满足int
类型要求,而且视图函数中该参数拿到后也是Python中对应的int
类型。
Django中有一些内置的URL类型转换器,都在from django.urls import converters
中,可自行查看,常用的有以下几种:
int
:必须是0到9之间的一个或多个数字。str
:除了斜杠/
之外的一个或多个任意字符。(也是默认的参数类型)uuid
:一个uuid字符串,可以使用Python内置库方法来生成import uuid;uuid.uuid4()
。slug
:满足正则[-a-zA-Z0-9_]+
的所有字符串。path
:一个或多个任意字符(斜杠/
也可以)。
4、自定义URL类型转换器
自定义URL类型转换器其实也是根据内置的转换器写法来进行自定义的,可以参考from django.urls import converters
中内置转换器的源码,一个自定义的url类型转换器通常分为三部分:regex
正则表达式(转换规则)、to_python
函数(将url中的参数处理之后再传入视图函数)和to_url
函数(将参数处理之后再拼接到url之中)。定义好转换器之后,需要使用from django.urls import register_converter
将转换器注册到Django中。
示例代码如下:
"""直接定义在urls.py中"""
from django.urls import path
from . import views
from django.urls import register_converter
# 自定义URL类型转换器:将URL中类似python+django+flask的字符串
# 转换为以+分隔的列表传入视图函数中,反转为URL时再以+拼接到URL中
class CategoryConverter:
regex = r"w+|w+(+w+)+"
def to_python(self, value):
# 此方法是将url中的参数处理之后再传入对应的视图函数中
# value:python+django+flask
return value.split("+")
def to_url(self, value):
# 此方法是将在reverse反转时给url传入的参数处理之后再放入url中,形成最终的url
# value:["python", "django", "flask"]
return "+".join(value)
# 注册自定义的转换器
register_converter(CategoryConverter, "cate")
urlpatterns = [
path("", views.article),
# 使用自定义的转换器
path("list/<cate:categories>/", views.article_list, name="list")
]
"""views.py"""
def article_list(request, categories):
# 如果访问:http://127.0.0.1:8000/article/list/python+django+flask/
# 打印结果为:/article/list/python+django+flask/
print(reverse("list", kwargs={"categories": categories}))
text = "文章分类是:{}".format(categories)
return HttpResponse(text)
注:url转换器定义的文件一定要在运行时得到执行,不然无法进行注册,可以放在urls.py
中,也可以将定义转换器的文件在包的__init__.py
文件中进行导入。
三、URL分层管理
当新建的app较多时,不宜将所有的URL映射都放在主app(该app通常与项目同名)的urls.py
文件中,就像app下的views.py
、models.py
等文件(使用命令窗口的命令创建app时,会自动生成这些文件),用专门的文件来管理各自的功能,推荐在app下新建一个urls.py
文件,用来专门管理当前app的所有URL映射关系,这样也方便开发人员的维护和管理。
URL的分层管理需要注意以下两点:
- 在子app下新建一个
urls.py
(文件名称可以自己取,但是通常就命名为urls.py
即可,方便辨认),然后在里面同样定义一个urlpatterns
变量。注意,这个urlpatterns
中的URL字符串不用再添加子app对应的URL前缀了。 - 在主app的
urls.py
的urlpatterns
变量中添加一个URL映射,第一个参数为子app的URL前缀,第二个参数为from django.urls import include
包装的子app对应的urls.py
文件路径(此路径为相对于项目根目录的相对路径)。
"""主app的urls.py文件内容"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
# 这里只需要添加app的url前缀和对应urlpattterns变量所在文件的路径即可
path("book/", include("book.urls"))
]
"""子app“book”目录中的urls.py文件内容"""
from django.urls import path
from .views import book, book_detail
# 这里不用再添加app的url前缀了
urlpatterns = [
path("", book),
path("detail/<book_id>/", book_detail)
]
四、URL反转
URL反转表示根据一个名称就可以得到对应的完整URL字符串,而不用自己每次手动编写完整的URL,如果项目中许多地方都会用到某个URL,就可以考虑使用URL反转了,URL反转需要用到from django.shortcuts import reverse
,基本用法为:使用形如reverse(url_name)
即可得到对应的URL。
1、URL命名
想要给URL命名,在配置URL映射关系时,给path
函数指定name
参数即可,然后使用reverse(url_name)
函数反转就可以得到对应名称的URL了。
"""配置URL映射关系"""
from django.urls import path
from hello_world import views
urlpatterns = [
# 使用name参数给当前URL指定名称
path("index/", views.index, name="index")
]
"""视图函数定义"""
from django.shortcuts import render, reverse
def index(request):
# 获取名称为index的URL,打印结果为:/index/
print(reverse("index"))
return render(request, "index.html")
2、app应用命名空间和实例命名空间
配置app应用命名空间是为了规避产生同名URL的情况,app应用命令空间的配置是直接在对应app目录的urls.py
中定义一个app_name=xxx
的变量即可,反转时加上对应的app_name
即可,如reverse("app_name:url_name")
。
from django.urls import path
from . import views
# 配置app应用命令空间
app_name = "front"
urlpatterns = [
path("", views.index),
# 给URL指定名称
path("login/", views.login, name="login"),
]
from django.http import HttpResponse
from django.shortcuts import redirect, reverse
def index(request):
username = request.GET.get("username")
if username:
return HttpResponse("前台 首页!")
else:
# 反转时使用“app应用命名空间:url名称”的格式
return redirect(reverse("front:login"))
使用实例命名空间是为了规避不同的URL映射到了相同的urls.py
中,即不同的实例映射到了同一个app上,此时反转得到的URL可能就不是我们想要的了。实例命名空间配置是在主app中配置子app的URL前缀时的include
中指定其namespace
参数,然后再使用request.resolver_match.namespace
获取当前实例命名空间即可,此时就不再使用应用命名空间了(虽然配置了)。注意,指定实例命名空间时必须同时指定应用命名空间,单独指定实例命名空间是不可以的,程序会报错。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
# 在include中指定namespace命名空间
# 不同的前缀指向了相同的urls.py文件,所以只用app应用空间是不够的
# 需要再指定namespace命名空间,即实例命名空间
path("cms1/", include("cms.urls", namespace="cms1")),
path("cms2/", include("cms.urls", namespace="cms2")),
path("", include("front.urls"))
]
from django.http import HttpResponse
from django.shortcuts import redirect, reverse
def index(request):
username = request.GET.get("username")
if username:
return HttpResponse("cms 首页!")
else:
# 获取当前所处的实例命名空间
current_namespace = request.resolver_match.namespace
return redirect(reverse("{}:login".format(current_namespace)))
五、其他函数
1、django.urls.include
include
有三种用法:include(module, namespace=None)
、include((pattern_list, app_namespace), namespace=None
和include(pattern_list)
,它们的参数使用如下:
module
:子URL的模块字符串(即对应app的urls.py
文件路径)。namespace
:实例命名空间。需要注意的是,指定实例命名空间时,需要同时指定对应的app应用命名空间(在对应app的urls.py
中配置变量app_name
)。pattern_list
:一个子URL的模块字符串或者path对象的列表,后者相当于是在path
的第一个参数指定了子app的URL前缀,如果这个子app下有多个URL,就可以直接放在这个pattern_list
列表中。app_namespace
:应用命名空间。注意,应用命名空间既可以在include
中指定,也可以通过在子app的urls.py
中配置app_name
的方式来指定。namespace
:实例命名空间。
2、django.shortcuts.reverse
reverse
用于url的反转,通常只需要在path
中指定name
参数即可(必要的时候也可以指定app应用命名空间或实例命名空间)。如果url中含有参数,则需要在使用reverse
时传入kwargs
参数,以字典的形式将参数传入。
注:如果是查询字符串的参数,则需要reverse
反转之后自己手动进行拼接url字符串。
"""视图函数"""
from django.http import HttpResponse
from django.shortcuts import reverse, redirect
def index(request):
username = request.GET.get("username")
if username:
return HttpResponse("首页")
else:
# detail对应的url映射为:path("detail/<article_id>/", article_detail, name="detail"),
detail_url = reverse("detail", kwargs={"article_id": 333})
# 如果需要传入查询字符串,则需要自己手动进行拼接,如:detail_url += "?username=xxx"
return redirect(detail_url)
def article_detail(request, article_id):
text = "文章的id是:{}".format(article_id)
return HttpResponse(text)