Django笔记:URL映射

内容摘要
本文主要记了一些Django中URL映射相关的知识点,包括URL映射关系配置、URL传参、URL反转等,本文为学习笔记,仅供参考。
文章正文

本文主要记了一些Django中URL映射相关的知识点,包括URL映射关系配置、URL传参、URL反转等,本文为学习笔记,仅供参考。

一、URL映射关系配置

URL映射关系默认是配置在主app下urls.py中的urlpatterns列表中,如果想要自己指定配置映射关系所在文件,只需要修改主app中settings.py文件中的ROOT_URLCONF配置项即可,但是urlpatterns这个变量是固定的,无法修改,所以修改ROOT_URLCONF配置项之后,Django就会到你指定的文件中查找urlpatterns变量,并根据这个列表中的映射关系来进行URL的映射。
urlpatterns列表中的元素是一个经过pathre_path函数包装后的结果,这两个函数都在django.urls中,直接导入使用即可。这两个函数的用法都是相似的,不同之处在于,re_pathroute参数(即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.pymodels.py等文件(使用命令窗口的命令创建app时,会自动生成这些文件),用专门的文件来管理各自的功能,推荐在app下新建一个urls.py文件,用来专门管理当前app的所有URL映射关系,这样也方便开发人员的维护和管理。
URL的分层管理需要注意以下两点:

  • 在子app下新建一个urls.py(文件名称可以自己取,但是通常就命名为urls.py即可,方便辨认),然后在里面同样定义一个urlpatterns变量。注意,这个urlpatterns中的URL字符串不用再添加子app对应的URL前缀了。
  • 在主app的urls.pyurlpatterns变量中添加一个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=Noneinclude(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)
代码注释
[!--zhushi--]

作者:喵哥笔记

IDC笔记

学的不仅是技术,更是梦想!