Django框架之路由层

一、路由匹配

1、什么是路由

在计算机领域中,路由(Routing)通常指的是确定数据包在网络中传输的路径的过程。当数据包从源主机发送到目标主机时,路由是决定数据包应该沿着哪条路径传输的方法。路由器是负责执行这种路由功能的设备。

在Web开发中,路由通常指的是将特定的URL请求映射到相应的处理程序或控制器的过程。在Web应用程序中,路由器负责根据用户请求的URL确定应该调用哪个处理程序来处理请求。通过定义路由规则,可以实现将特定URL请求与特定功能或页面关联起来。

例如,在一个基于MVC(Model-View-Controller)架构的Web应用程序中,路由器可以根据用户请求的URL确定应该调用哪个控制器的哪个方法来处理请求,然后将结果返回给用户。

通俗来说,路由可以看成是跟在 ip 和 port 之后的地址

在这里插入图片描述

2、url () 方法

# 示例
urlpatterns = [
    url('admin/', admin.site.urls),
    url('login/', views.login_func),
    url('register/$', views.register_func),
]
  • url方法第一个参数是路径参数

(1)路径参数相似

  • 第二个无法跳转到 testadd

  • 只要第一个参数能够匹配到内容,就会立刻停止匹配,执行视图函数

# 路由匹配
path('test', views.test),
path('testadd', views.testadd),

(2)解决路径参数相似问题方案

  • 方式一:修改templates文件名
  • 方式二:用app02/login.html路径取代login.html,指定路径即可

3、路由匹配注意事项

  • 不需要在路由前面添加反斜杠 “/”, 因为每个 url 自带

  • 建议在正则表达式之前添加原生字符 “r”

  • 每个由正则匹配成功并通过分组捕获的参数都会作为一个普通的Python字符串传递给视图函数

  • 如果路由结尾没有"/“, 在第一次正则匹配机制没有匹配到想要的内容时, 会在匹配字符后加一个”/", 然后Django内部重定向在匹配一次

4、取消自动添加斜杠 "/"

  • 测试了如果路由后面不添加"/"一共会匹配两次
  • 而自动添加斜杠这种操作是可以取消的
# setting.py 文件
APPEND_SLASH = False  # 默认 True 自动添加斜杠

建议自动添加

5、完整的路由匹配

  • 完整版的路由匹配格式
path('test/', views.test),
  • 匹配首页的路由格式
path('', views.test),

二、分组命名匹配

1、无名分组

  • 分组就是将某段正则表达式用()括起来
  • 无名分组就会将括号内正则表达式匹配到的内容当做位置参数传递给后边的视图函数
path('login/', views.login_func)

# 无名分组 
re_path(r'^login/(\d+)/$', views.login_func)
# 视图函数
def login_func(request,*args):
    print(args)
    return Httpresponse(args)

2、有名分组

  • 将正则表达式分组捕获到的内容定义一个名字
  • 有名分组会将括号内正则表达式匹配到的内容当做关键字参数传递给后面的视图函数
# 有名分组
re_path(r'^testadd/(?P<year>\d+)', views.testadd),  # year就是关键字
def testadd(request, year):
    print(year)

    return HttpResponse("testadd")

3、无名有名是否可以结合使用

  • 无名分组和有名分组不能混用
# 无名有名混合使用
re_path(r'^index/(\d+)/(?P<year>\d+)/', views.index),

# 官方说不能混着用, 混着用只能取到有名分组捕获的值
# 只要不混着用,有名分组和无名分组支持多个相同类型的传参
  • 但是同一个分组可以使用多次
re_path(r'^test/(\d+)/(\d+)/',views.test),  # 多次使用无名
re_path(r'^test/(?P<year>\d+)/(?P<age>\d+)/(?P<year>\d+)/',viewsre_path.test),  # 多次使用有名
def index(request,*args):
    return HttpResponse("index")
def test(request, *args, **kwargs):
    print(args) # ()
    print(kwargs)  # {'year': '21', 'age': '12', 'month': '32'}
    return HttpResponse("test")

三、反向解析

1、什么是反向解析

反向解析(Reverse URL resolution)是一个在Web开发中常用的概念,特别是在使用框架如Django等时。它指的是根据URL模式(URL patterns)和视图名称(view names)来动态地生成URL的过程。

通俗的来说:

反向解析就是通过一些方法得到一个结果, 该结果可以直接访问对应的 url 并触发视图函数

2、反向解析的作用

(1)减少硬编码URL

通过使用反向解析,开发者可以避免在代码中硬编码URL,而是使用视图名称来动态生成URL。这样做可以提高代码的可读性和可维护性,同时减少因URL变更而需要手动修改多处代码的风险。

(2)简化URL配置

通过将URL模式与视图名称关联,可以更简洁地配置URL。这样可以使URL配置更具结构化,易于管理和扩展。

(3)提高灵活性

反向解析使得在不同环境中动态生成URL变得更加灵活。例如,在开发环境和生产环境中URL可能会有所不同,使用反向解析可以根据当前环境动态生成正确的URL, 让 html 界面上的连接地址做到动态解析。

(4)增加可重用性

通过使用反向解析,可以在不同的视图中重复使用相同的URL模式,而不必担心URL的变化。这提高了代码的可重用性和模块化程度。

3、如何使用反向解析

  • 给路由与视图函数对应关系添加一个别名 (名字由自己指定, 只要不冲突即可)
# 路由层
path('login/', views.login_func,name='login_name')
  • 根据该别名可动态解析出一个结果, 该结果可以直接访问到对应的路由
# 前端中使用(模板层)
<a href="{% url 'login_name' %}">登入</a>

# 后端中使用(视图层)
from django.shortcuts import reverse
url = reverse('login_name')

ps : 使用redirect( ) 括号内也可以直接写别名

4、无名分组反向解析

在Django中,无名分组反向解析是指在URL模式中使用无名分组(non-named groups)来捕获URL中的一部分,并在视图函数中使用这些捕获的值。这种技术在一些情况下很有用,尤其是当需要捕获一些动态变量但不需要为它们命名时。

下面是一个简单的示例,展示了如何在Django中使用无名分组进行反向解析:

假设有以下URL模式

# 无名分组反向解析
re_path(r'^index/(\d+)/', views.index, name='xxx'),

然后在视图函数代码中可以使用reverse()函数来反向解析这个URL,如下所示:

def home(request):
    print(reverse('xxx', args=(1,)))
    return render(request, 'home.html')

在这个例子中,args参数中的值会按顺序匹配到URL模式中的无名分组。

前端使用:

<a href="">{% url 'xxx' 123 %}</a>

当我们跳转到指定路由地址的时候,在后面携带参数,这样就可以避免像上面一样,抛出异常,匹配不到。

伪代码:

# 路由
re_path(r'^edit/(\d+)/', views.edit, name='xxx')
# 视图函数
def edit(request,edit_id):
    reverse('xxx',args=(edit_id,))
# 前端页面
{%for user_obj in user_queryset%}
	<a href="{% url 'xxx' user_obj.id %}">编辑</a>
{%endfor%}

5、有名分组反向解析

  • 路由层配置
re_path(r'^login/(?P<id>\d+)/', views.login_func,name='login_name')
  • 视图层配置
from shortcuts import reverse
url = reverse('login_name',kwargs=(id:111))  # 随便给个数字
url= =
reverse('login_name',args=(111,))      # 也可以这样写(推荐)
  • 模板层配置
<a href="{% url 'login_name' id=111 %}">登入</a>  # 随便给个数字
<a href="{% url 'login_name' 11 %}">登入</a>      # 也可以这样写(推荐)

由上面的视图层与模板层的第二种书写方式可以看出 : 无名有名都可以使用一种反向解析形式 : 就是无名反向解析

四、路由分发

1、路由分发简介

django是专注于开发应用的,当一个django项目特别庞大的时候, 所有的路由与视图函数映射关系全部写在一个 urls.py 里面很明显太冗余并且不便于管理

其实django中的每一个应用都可以有自己的 urls.py、static文件夹、templates文件夹, 基于上述特点, 使用django做分组开发非常的简便

每个人只需要写自己的应用即可, 最后由组长统一汇总到一个空的django项目中然后使用路由分发将多个应用关联到一起。

  • 利用路由分发之后,总路由不再干路由与视图函数的直接对应关系,而是做一个分发处理,识别当前url是属于哪个应用下的,直接分发 ,并且应用路由重名也无关要紧

2、路由分发设置

  • 总路由 : urls.py
from django.contrib import admin
from django.urls import path,include

# 写法一:给每个app的url设置不同的别名
from app01 import urls as app01_ulrs
from app02 import urls as app02_ulrs
urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include(app01_ulrs)),  # 只要url前缀是app01开头,全部交给app01处理
    path('app02/', include(app02_ulrs)),  # 只要url前缀是app02开头,全部交给app02处理
    path('app03/', include(app03_ulrs))   # 只要url前缀是app03开头,全部交给app03处理
]

    
# 写法二:转发url列表
    extra_patterns = [
    path('reports/', credit_views.report),
    path('reports/<int:id>/', credit_views.report),
    path('charge/', credit_views.charge),
]

urlpatterns = [
    path('', main_views.homepage),
    path('help/', include('apps.help.urls')),
    path('credit/', include(extra_patterns)),
]

# 写法三:转发app下的urls(推荐使用)
	urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.ulrs')),
    path('app02/', include('app02.ulrs')),
    path('app03/', include('app03.ulrs'))
]

# 子路由:
    # app01 urls.py
    from django.urls import path
    from app01 import views

    urlpatterns = [
        path('reg/', views.reg),
    ]

    # app02 urls.py
    from django.urls import path
    from app02 import views

    urlpatterns = [
        path('reg/', views.reg),

    ]
  • 每个应用路由(子路由)文件
  • app01 urls.py
from django.contrib import admin
from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('home/',views.home_func),
    path('index/',views.index_func),
    path('edit/',views.edit_func),
]
  • app02 urls.py
from django.contrib import admin
from django.urls import path,re_path
from app02 import views

urlpatterns = [
    path('home/',views.home_func),
    path('index/',views.index_func),
    path('edit/',views.edit_func),
]

3、include () 补充

在Django中,你可以通过include()函数传递额外的参数给被包含的URLconf。这在需要在包含的URLconf中使用一些额外的参数时非常有用。

下面是一个示例,演示如何通过include()函数传递额外的参数:

假设你有一个应用程序myapp,并且你想要在包含myapp的URLconf时传递一个名为extra_param的额外参数。首先,在myapp应用程序的urls.py文件中定义一个带有额外参数的URL模式:

from django.urls import path
from . import views

urlpatterns = [
    path('myview/', views.my_view, name='my_view'),
]

然后,在项目的主urls.py文件中,你可以通过include()函数传递额外的参数给myapp的URLconf:

from django.urls import path, include

extra_param = 'example_param'

urlpatterns = [
    path('myapp/', include('myapp.urls', namespace='myapp_namespace', kwargs={'extra_param': extra_param})),
]

在这个示例中,kwargs={'extra_param': extra_param}部分传递了一个名为extra_param的额外参数给被包含的URLconf。在myapp的视图函数中,你就可以通过kwargs来访问这个额外参数。

这样,你就可以在包含的URLconf中使用这个额外参数,以实现更灵活和定制化的功能。

五、名称空间

1、为什么使用名称空间

当多个应用设置了相同的别名,,在反向解析的时候前面路由会被后面的路由覆盖,那么就无法触发前面路由对应的视图函数。 而正常情况下, 反向解析是无法自动识别前缀的,为了避免这种错误,引入了名称空间。

其实只要保证名字不冲突,就没有必要使用名称空间

2、应用命名空间与实例命名空间

(1)应用命名空间

应用命名空间也称为app命名空间。

我们思考这么一个问题,假设下面的情况:

  • appA,有一条路由A,它的name叫做'index'

  • appB,有一条路由B,它的name也叫做'index'

这种情况完全是有可能的,甚至还常见!

请问,你在某个视图中使用reverse('index',args=(...))或者在模板中使用{% url 'index' ... %},它最终生成的URL到底是路由A还是路由B呢?

  • 不管是哪个,肯定都不好,因为我们需要确定状态的路由,而不是混乱的。
  • 之所以造成这种情况,根本上还是各个app之间没有统一的路由管理,实际上也不可能有。

解决方案:

  • Django提供了一个叫做app_name的属性,帮我们实现应用级别的命名空间,这样,虽然大家都叫‘大伟’,但一个是‘张大伟’,一个是‘王大伟’,自然就分清楚了甲乙丙丁。
from django.urls import path
from . import views

app_name = 'your_app_name'   # 重点是这行!

urlpatterns = [
    ...
]

如何使用:

我么只需要在app自己本身的urls.py文件内,添加一行app_name = 'your_app_name'即可。注意不是在根路由文件中。一般就和自己的app同命即可,因为项目里面不会有2个同样名字的app。

# 视图函数中
reverse('your_app_name:index',args=(...))

# 模板语法中
{% url 'your_app_name:index' ... %}

注意your_app_name和index之间是冒号分隔。

(2)实例命名空间

同一个app可以用不同的url来映射,比如下面的代码,两个url都指向同一个app,也就是所谓的同一个app有两个实例。第一个实例是cms1,第二个实例是cms2。

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('',include('front.urls')),

    # 同一个app下有两个实例
    path('cms1/',include('cms.urls')),
    path('cms2/',include('cms.urls')),
]

1个app可以创建多个实例,可以使用多个url映射同一个app,所以这就会发生一个问题:反向解析的时候,如何根据不同的url来区分不同的实例。如果使用命名空间,那就会发生混淆,为了避免这个问题,我们可以使用实例命名空间。实例命名空间非常简单,只要在include函数中传递一个namespace变量即可,示例代码如下:

urlpatterns = [
    path('',include('front.urls')),
    path('cms1/',include('cms.urls',namespace='cms1')),
    path('cms2/',include('cms.urls',namespace='cms2')),
]

以后再做反转的时候,就可以以实例命名空间来指定具体的url,示例代码如下:

def index(request):
    username = request.GET.get("username")
    if username:
        return HttpResponse('CMS首页')
    else:
        current_namespace = request.resolver_match.namespace
        return redirect(reverse("%s:login"%current_namespace))

3、解决路由解析一致问题

(1)解决方案一

  • 解决的思路就是给各自的app创建一个名称空间,让彼此解析只在自己的名称空间内解析
# app01/urls.py
from django.urls import path
from app01.views import index

# 在路由分发文件中给当前APP创建一个app的应用命名空间
app_name="app01"

urlpatterns = [
    path("index/", index, name="index_view")
]

# app01/view.py
from django.shortcuts import render, reverse, HttpResponse

def index(request):
  	# 解析路由映射关系时指定当前APP名字
    print(f"from app01 view :>>>>> {reverse('app01:index_view')}")
    return HttpResponse("FROM APP01 INDEX_VIEW")
# app02/urls.py
from django.urls import path
from app02.views import index

# 在路由分发文件中给当前APP创建一个app的应用命名空间
app_name="app02"

urlpatterns = [
    path("index/", index, name="index_view")
]

# app02/view.py
from django.shortcuts import render, reverse, HttpResponse

def index(request):
  	# 解析路由映射关系时指定当前APP名字
    print(f"from app02 view :>>>> {reverse('app02:index_view')}")
    return HttpResponse("FROM APP02 INDEX VIEW")
# 根路由 urls.py
from django.contrib import admin
from django.urls import path
from django.urls.conf import include
urlpatterns = [
    path('admin/', admin.site.urls),
 	  # 路由分发,将 app01 的前缀的路由分发
    # 给当前路由分发指定实例名称空间
    path("app01/", include(arg="app01.urls", namespace="app01")),
    # 路由分发,将 app02 的前缀的路由分发
  	# 给当前路由分发指定实例名称空间
    path("app02/", include(arg="app02.urls", namespace="app02")),
]
  • 再次访问前端路由
# http://127.0.0.1:8000/app01/index

# http://127.0.0.1:8000/app02/index
  • 此时输出如下
# app02/views.py
# from app02 view :>>>> /app02/index/
# [28/Feb/2024 05:08:02] "GET /app02/index/ HTTP/1.1" 200 21

# app01/views.py
# from app01 view :>>>>> /app01/index/
# [28/Feb/2024 05:08:06] "GET /app01/index/ HTTP/1.1" 200 21
  • 我们会发现这两个app解析到了各自的名称空间中的路由映射关系

(2)解决方案二

  • 解决的思路是在总路由分发时给定应用名称空间
# app01/urls.py
from django.urls import path
from app01.views import index

urlpatterns = [
    path("index/", index, name="index_view")
]

# app01/view.py
from django.shortcuts import render, reverse, HttpResponse

def index(request):
  	# 解析路由映射关系时指定当前APP名字
    print(f"from app01 view :>>>>> {reverse('app01:index_view')}")
    return HttpResponse("FROM APP01 INDEX_VIEW")
# app02/urls.py
from django.urls import path
from app02.views import index

urlpatterns = [
    path("index/", index, name="index_view")
]

# app02/view.py
from django.shortcuts import render, reverse, HttpResponse

def index(request):
    # 解析路由映射关系时指定当前APP名字
    print(f"from app02 view :>>>> {reverse('app02:index_view')}")
    return HttpResponse("FROM APP02 INDEX VIEW")
# 根路由 urls.py
from django.contrib import admin
from django.urls import path
from django.urls.conf import include
urlpatterns = [
    path('admin/', admin.site.urls),
 	  # 路由分发,将 app01 的前缀的路由分发
    # 给当前路由分发指定实例名称空间
    path("app01/", include(arg=("app01.urls","app01"), namespace="app01")),
    # 路由分发,将 app02 的前缀的路由分发
  	# 给当前路由分发指定实例名称空间
    path("app02/", include(arg=("app02.urls","app02"), namespace="app02")),
]
  • 再次访问前端路由
# http://127.0.0.1:8000/app01/index

# http://127.0.0.1:8000/app02/index
  • 此时输出如下
# app02/views.py
# from app02 view :>>>> /app02/index/
# [28/Feb/2024 05:08:02] "GET /app02/index/ HTTP/1.1" 200 21

# app01/views.py
# from app01 view :>>>>> /app01/index/
# [28/Feb/2024 05:08:06] "GET /app01/index/ HTTP/1.1" 200 21
  • 我们会发现这两个app解析到了各自的名称空间中的路由映射关系

六、伪静态

1、简介

静态网页:数据是写死的,万年不变

伪静态(pseudo-static):通常用于描述一种技术,其中网站的URL看起来像静态URL,但实际上是动态生成的内容。

在这种情况下,服务器会根据静态的URL结构来处理请求,但实际上内容是动态生成的。这通常通过将动态URL转换为静态URL来实现,以便更好地索引和排名网站内容。

2、伪静态的应用场景

在 Django 或其他 Web 框架中,可以使用路由和视图来实现伪静态URL。通过适当的配置,可以让网站的URL看起来更友好和易于理解,同时保持动态内容的生成和管理能力。这有助于提升用户体验和搜索引擎优化。

伪静态技术在以下几个应用场景中非常有用:

  1. SEO优化:搜索引擎更喜欢静态URL,因为它们通常更易于理解和索引。使用伪静态技术可以让动态生成的内容在搜索引擎中更好地展示和排名。
  2. 用户体验:静态URL更易于记忆和分享,用户更倾向于点击看起来更清晰和简洁的URL。伪静态技术可以帮助改善用户体验,使用户更容易访问和分享网站内容。
  3. 防止URL过长:动态URL可能会包含大量参数和查询字符串,导致URL过长且难以阅读。通过伪静态技术,可以将动态URL转换为简洁的静态形式,提高URL的可读性。
  4. 旧链接重定向:当网站重构或更改URL结构时,为了保持旧链接的有效性,可以使用伪静态技术创建旧链接到新链接的重定向规则,以确保用户访问旧链接时能够正确地跳转到新页面。

总的来说,伪静态技术可以帮助网站提升SEO、改善用户体验、简化URL结构、以及处理旧链接重定向等方面的需求。这种技术在许多网站和Web应用程序中都得到广泛应用。

ps:再怎么伪装还是干不过RMB玩家

七、虚拟环境

  • 在正常开发中,会给每一个项目配备一个该项目独有的解释器环境

  • 该环境内只有该项目用到的模块,用不到一概不装

  • linux:缺什么才装什么

1、什么是虚拟环境

虚拟环境 (virtual environment) 就是一个虚拟化,从电脑独立开辟出来的环境。通俗的来讲,虚拟环境就是借助虚拟机docker来把一部分内容独立出来,我们把这部分独立出来的东西称作“容器”,在这个容器中,我们可以只安装我们需要的依赖包,各个容器之间互相隔离,互不影响。譬如,本次学习需要用到Django,我们可以做一个Django的虚拟环境,里面只需要安装Django相关包就可以了,需要Scrapy库,就在开辟一个独立空间来学习Scrapy库相关就行了。

但是虚拟环境不要创建太多,是需要消耗硬盘空间的

2、扩展

每一个项目都需要用到很多模块,并且每个模块版本可能还不一样,该如何安装?
开发项目必备requirement.txt文件,里面书写了该项目所有的模块及版本,只需要输入pip install -r requirement.txt安装里面的模块及版本即可。

3、模块文件导出和安装

(1)导出项目模块文件
pip freeze > requirements.txt
  • 这行命令的含义是 “freeze”(冻结)当前环境中的所有已安装包及其版本信息,并将其输出重定向到 requirements.txt 文件中。
  • 执行此命令后,requirements.txt 将列出你项目所需的全部依赖及其对应版本号。
(2)安装项目模块文件
pip install -r requirements.txt
  • 这将按照 requirements.txt 中列出的顺序及版本要求安装相应的 Python 包。

八、Django版本区别

1、路由匹配规则

  • Django1.x路由层使用的是url方法
  • 在Django2.x版本以后在路由层使用的是path方法
    • url() 第一个参数支持正则
    • path() 第一个参数不支持正则,写什么就匹配到什么

2、正则匹配规则

  • 在Django2.x以后也可以使用正则表单式,但是使用的方法是re_path
from django.urls import path, re_path

re_path(r'^fuc/(?P<year>\d+)', views.func)
# 等价于
url(r'^fuc/(?P<year>\d+)', views.func)

3、路径转换器

(1)简单介绍

urls.py

 urlpatterns = [
    path('index/<int:id>', index),
 ]

views.py

from django.shortcuts import HttpResponse

def index(request, id):
    print(id,type(id))  # 111 <class 'int'>
    return HttpResponse('index')

<int:id>相当于一个有名分组,其中int是django提供的转换器,相当于正则表达式,专门用于匹配数字类型,而id则是我们为有名分组命的名,并且int会将匹配成功的结果转换成整型后按照格式(id=整型值)传给函数index。

(2)五种转换器

  • str
    • 匹配除了 ‘/’ 之外的非空字符串。
    • 如果表达式内不包含转换器,则会默认匹配字符串。
  • int
    • 匹配 0 或任何正整数。返回一个 int 。
  • slug
    • 匹配任意由 ASCII 字母或数字以及连字符和下划线组成的短标签。
    • 比如,building-your-1st-django-site 。
  • uuid
    • 匹配一个格式化的 UUID 。为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。
    • 比如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID 实例。
  • path
    • 匹配非空字段,包括路径分隔符 ‘/’ 。
    • 它允许你匹配完整的 URL 路径而不是像 str 那样匹配 URL 的一部分。

4、自定义转换器

(1)简单介绍

  • 转换器是一个类,包含如下内容:

    • 字符串形式的 regex 类属性。

    • to_python(self, value) 方法,用来处理匹配的字符串转换为传递到函数的类型。如果没有转换为给定的值,它应该会引发 ValueErrorValueError 说明没有匹配成功,因此除非另一个 URL 模式匹配成功,否则会向用户发送404响应。

    • 一个 to_url(self, value) 方法,它将处理 Python 类型转换为字符串以用于 URL 中。如果不能转换给定的值,它应该引发 ValueErrorValueError 被解释为无匹配项,因此 reverse() 将引发 NoReverseMatch,除非有其他 URL 模式匹配。

Changed in Django 3.1:

支持引发 ValueError 以表示没有匹配项被添加。

(2)自定义转换器示例

  • 在app01下新建文件 path_ converters.py(文件名可以随意命名)

    class FourDigitYearConverter:
        # 此属性名为正则表达式(regex),用于匹配四位数的年份字符串
        regex = r'[0-9]{4}' 
    
        def to_python(self, value):
            """
            将接收到的字符串值解析为Python整数类型表示四位数的年份。
            示例:输入 "2024" 会转换为 2024
            """
            return int(value)
    
        def to_url(self, value):
            """
            根据给定四位数年份(value)将其格式化为URL安全的四位数字形式,例如 "2024" -> "2024".
            注意这里的 "匹配的regex是四个数字" 应更改为 "此方法针对四位数的年份字符串"
            
            例如:输入 "2024" 会返回 "2024", 保持四位数且无前导零。
            """
            return '%04d' % value
    

(3)使用自定义转换器

  • 在urls.py中,使用register_converter() 将其注册到URL配置中:

    # 引入所需的模块和从当前目录下的views.py导入视图函数
    from django.urls import path, register_converter
    from . import converters, views
    
    # 在Django应用中注册自定义的日期转换器类,即位于converters文件中的FourDigitYearConverter
    # 并为其指定别名 'yyyy', 这个别名可以方便地在URL模式中引用该转换器
    register_converter(converters.FourDigitYearConverter, 'yyyy')
    
    # 配置URL模式,其中:
    #   path('/articles/2003/', views.special_case_2003) - 特殊情况处理,当访问 '/articles/2003/' 时调用 special_case_2003 视图函数
    #   path('articles/<yyyy:year>/', views.year_archive) - 处理常规年份归档页面请求,
    #       其中 '<yyyy:year>' 表示 URL 模板中 '<year>' 前面带有 'yyyy' 转换器别名,
    #       Django将在实际匹配到的 URL 字符串中找到四位数的年份并传递给 views.year_archive 视图函数作为参数
    
    urlpatterns = [
        path('articles/2003/', views.special_case_2003),
        path('articles/<yyyy:year>/', views.year_archive),
        ...
    ]
    
    

(4)转换器使用示例

  • app01/path_converters.py
class MonthConverter:
    regex = '\d{2}'  # 属性名必须为regex

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return value  # 匹配的regex是两个数字,返回的结果也必须是两个数字
  • urls.py
from django.contrib import admin
from django.urls import path
from app01 import views

register_converter(converters.FourDigitYearConverter, 'yyyy')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('articles/<int:year>/<int:month>/<slug:other>/', views.article_detail)
    # 针对路径http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配出参数year=2009,month=123,other='hello'传递给函数article_detail
]
  • app01/views.py

    from django.shortcuts import render,HttpResponse,reverse
    from django.urls import reverse
    
    def article_detail(request, year, month, other):
        print(year, type(year))
        print(month, type(month))
        print(other, type(other))
        print(reverse('article_detail', args=(1988, 12, 'hello')))  # 反向解析结果/articles/1988/12/hello/
    
        '''
        2009 <class 'int'>
        12 <class 'int'>
        hello <class 'str'>
        /articles/1988/12/hello/
        '''
        return HttpResponse('xxxx')
    
  • 测试

    # 在浏览器输入http://127.0.0.1:8000/articles/2009/12/hello/
    # path会成功匹配出参数year=2009,month=12,other='hello'传递给函数article_detail
    
    # 在浏览器输入http://127.0.0.1:8000/articles/2009/123/hello/
    # path会匹配失败,因为我们自定义的转换器mon只匹配两位数字,而对应位置的123超过了2位
    

5、级联更新

  • 模型层在1.x外键默认都是级联更新,级联删除的
  • 但是2.x 版本以后需要自己手动配置参数
    • 其中ForeignKey、OneToOneField会自动在字段后面加_id后缀
    • 还需要手动加上 on_delete=models.CASCADE on_delete=models.CASCADE 来标识级联更新和删除(Django1.x版本不需要手动添加)

,reverse
from django.urls import reverse

def article_detail(request, year, month, other):
print(year, type(year))
print(month, type(month))
print(other, type(other))
print(reverse(‘article_detail’, args=(1988, 12, ‘hello’))) # 反向解析结果/articles/1988/12/hello/

  '''
  2009 <class 'int'>
  12 <class 'int'>
  hello <class 'str'>
  /articles/1988/12/hello/
  '''
  return HttpResponse('xxxx')

- 测试

```python
# 在浏览器输入http://127.0.0.1:8000/articles/2009/12/hello/
# path会成功匹配出参数year=2009,month=12,other='hello'传递给函数article_detail
# 在浏览器输入http://127.0.0.1:8000/articles/2009/123/hello/
# path会匹配失败,因为我们自定义的转换器mon只匹配两位数字,而对应位置的123超过了2位

5、级联更新

  • 模型层在1.x外键默认都是级联更新,级联删除的
  • 但是2.x 版本以后需要自己手动配置参数
    • 其中ForeignKey、OneToOneField会自动在字段后面加_id后缀
    • 还需要手动加上 on_delete=models.CASCADE on_delete=models.CASCADE 来标识级联更新和删除(Django1.x版本不需要手动添加)
  • 20
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值