动态路由的写法_Django学习之路(4)MVC & MTV及Django路由系统

MVC & MTV

MVC是一种使用MVC(model view controller 模型-视图-控制器)设计创建web应用程序的模式,目的是为了实现代码分离,使得程序可以有不同的表现形式。

Model (模型)一般对应数据库操作、记录的增删改查

View (视图) 负责业务逻辑处理并且决定如何展示数据

Controller (控制器)负责处理用户交互的部分,控制器负责从视图读取数据,控制用户输入,并向模型发送数据

Django采用的是MTV框架,也是分成了几部分来降低各个部分之间的耦合性。主要分成Model(模型)、Template(模板)和View(视图)。但它的设计模式还是借鉴了MVC框架的思想,主要是将MVC中的V(视图)进一步分解为Django视图和Django模板两个部分,分别决定展现哪些数据和如何展现,使得Django的模板可以根据需要随时替换,而不仅仅限制于内置的模板。至于MVC得C(控制器)的部分,由Django框架的URLconf来实现。其实严格来说django是MVTC框架,只不过由于C(控制器)的部分,内部已经帮忙实现了,所以简化成MTV。

Django路由系统

下面来逐步学习Django框架得各个模块。先来学习一下路由系统。

在Django里,路由本质上是通过正则表达式来对用户请求得URL进行匹配,它主要分为静态路由和动态路由。我们下来看一下示例:

from django.urls import re_pathfrom app import viewsurlpatterns = [   re_path(r'articles/2020/$', views.special_2020),   re_path(r'^articles/(?P[0-9]{4})/$', views.year_case),   re_path(r'articles/(?P[0-9]{4})/(?P[0-9]{2})/(?P[\w-]+)/$', views.slug_case),]

这里写了三条路由规则。先看第一条规则,r'articles/2020/$' 这个就是正则表达式得匹配规则,后面就是我们前面几篇提到得匹配对应的视图函数。匹配规则前面的r表示使用的原始字符串,不对后面字符串里面的特殊字符做转义处理,比如'\n'。后面的$就是正则里面字符串结尾进行匹配,这里就是表示字符串以2020/结尾。

第二条规则就是以articles开头,后面(?P[0-9]{4})表示分组匹配并结尾,url分组匹配会得到一个字典作为非固定关键字参数传递给后面的视图函数,这里意思就是正则匹配到的四位数字作为year这个key的value。这里涉及到python函数参数的几个要点,我们来回顾一下:

1.参数分为以下类型,并且传参的位置依次为,位置参数,默认参数,非固定位置参数,关键字参数,非固定关键字参数。

其中非固定位置参数和非固定关键字参数,写法依次是*args,**kwargs。

2.关键字参数(包括非固定关键字参数)必须放在位置参数(包括非固定位置参数)后面。

如果我们请求的是http://127.0.0.1:8000/articels/2021,分组捕获的是一个字典{“year”:“2021”},并且是以year=2021的形式传给视图函数,因此关键字year不能写成别的。否则视图函数会报错。

def year_archive(request, year):  return HttpResponse(year)

如果希望参数year可以写成任意的,那么路由规则就需要改成:

 re_path(r'^articles/([0-9]{4})/$', views.year),

这时相当于正则匹配的结果变成位置参数传递给视图函数。

当然除了捕获URL的内容当成参数传递给对应的视图函数,我们还可以额外指定传入参数:

 re_path(r'^articles/(?P[0-9]{4})/$', views.year, {'version': 'v1.0'}),

这样的话,再写视图函数的时候,除了传入参数request和year,还需要传入version这个参数,不过由于version这个参数完全可以写入到视图函数里,所以用传入的方式显得有点鸡肋。

第三条规则与第二条规则不同在于后面多了一个slug,[\w-]+表示它匹配大小写字母数字和-,后面的+表示匹配一次或多次。

总结一下,第一条是静态路由,下面两条就是动态路由了。

这种写法需要掌握正则并且写起来比较丑陋,随着django的版本迭代,到了2.0版本之后,就不推荐使用re_path了,而是采用path写法,那么按照上面三条规则,我们使用新的写法来实现:

from django.urls import pathfrom app import viewsurlpatterns = [    path('articles/2020/$', views.special_2020),    path('articles//', views.year_case),    path('articles///(/', views.slug_case),]

在这里使用这种写法也就不再需要指明^和$了,而且匹配的路径转换器year、month、slug都已经帮忙封装好了,当然底层还是用的正则。但是这个写法已经清爽好看了很多。

下面就是django常用的一些路径转换器:

str - 匹配除了 / 之外的非空字符串。如果表达式内不包含转换器,则会默认匹配字符串。

int - 匹配 0 或任何正整数。返回一个 int 。

slug - 匹配任意由 ASCII 字母或数字以及连字符和下划线组成的短标签。比如,building- your-1st-django-site 。

uuid - 匹配一个格式化的 UUID 。为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。比如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个UUID实例。

path - 匹配非空字段,包括路径分隔符 '/' 。它允许你匹配完整的 URL 路径而不是像 str 那样匹配 URL 的一部分。

需要注意的str和path的区别在于path包括路径分隔符,而uuid和path这两种路径转换器也能给我们极大的便利。当然django还支持在复杂场景下自定义转换器,如下,我们可以自定义一个month路径转换器,因为它自带的还不能做正确的月份校验(1-12)。

class TwoDigitMonthConverter:    # 这里regex就是我们要做有效月份校验的正则匹配,表示以0开头尾数是1-9或者以1开头尾数是1-2。如果只是简单的写[0-9]{2},就会把00,99等这种错误的情况也匹配了。  regex = '0[1-9]|1[0-2]'    def to_python(self,value):    return int(value)      def to_url(self, value):    return '%02d' %value

在urls.py中使用register_converter()来注册自定义的转换器类:

from django.urls import path, register_converterfrom . import converters, viewsregister_converter(converters.TwoDigitMonthConverter, 'MM')urlpatterns = [    path('articles//', views.month_archive),]

完成以上步骤之后,我们来写一下month_archive这个视图函数,如下:

def month_archive(request, month):    return HttpResponse(month)

然后启动项目,访问http://127.0.0.1:8000/articles/12,发现能正常返回URL匹配的月份。

c897ec9f3ad1ac8de28a4407efbcec0b.png

但是当我们在浏览器访问http://127.0.0.1:8000/articles/13,就会抛出异常。说明我们上面自定义的路径转换器是可以正常工作的。

0c4d26b9b721767aa24c67d787c92799.png

以上就是自定义路径转换器的过程,如果你不想使用这种方式来实现,当然也还是可以使用上面提到的re_path方式来达到效果。

接下来就需要抛出一个问题,随着业务的发展,项目可能越做越大,可能有成千上万个url,难道所有的url都写到一个urls.py文件里吗?答案当然是否定的,一个项目肯定会按照不同的维度划分多个应用,我们不仅能把项目按照不同的功能、函数区分开,还可以把url也区分开,每个应用都可以用自己独立的url文件,当有多个app的时候,只需要在顶级url文件里include就可以了。比如我的项目有atricles和contact两个子应用,那就可以在这两个子应用的目录下创建urls.py文件,并且在项目的urls.py文件中以如下形式include就可以了。比如用户请求http://127.0.0.1:8000/articles/detail/01,项目urls.py文件应该是这样:

from django.urls import path, includeurlpatterns = [    path('articles/', include('article.urls')),    path('contact/', include('contact.urls')),  ...,]

而子应用articles的urls.py应该是这样:

from django.urls import pathfrom . import viewsurlpatterns = [    path('detail/', views.detail),]

在子应用的views.py文件中,我们写一个detail的视图函数,如下:

def detail(requests, article_id):    return HttpResponse('article_id: %s' % article_id)

浏览器访问http://127.0.0.1:8000/articles/detail/01,结果如下:

2f72b71352b443d936a7de0ee92d0635.png

另外,对于url中重复出现的部分,还可以进行聚合,这样能让url看起来更加的清晰。

比如有个credit的应用有如下规则:

from django.urls import pathfrom . import viewsurlpatterns = [    path('credit/reports/', views.report),    path('credit/charge/', views.charge),]

这里第一条路由规则和第二条路由规则都有重复出现的credit,那么我们改写成以下方式:

from django.urls import path, includefrom . import viewsextra_patterns = [    path('report/', views.report),    path('charge/', views.charge),]urlpatterns = [    path('credit/', include(extra_patterns)),]

这样就不用重复写credit这个,并且看起来更清爽了。

至此,Django的整个路由系统就基本学习完了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值