文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python自动化运维:Django之U

2023-01-31 02:58

关注

准备
首先新建一个Django 项目

django-admin startproject urlTest

进入manage.py所在目录后

./manage.py startapp app1
./manage.py startpap app2

此时我们新建了一个名为urlTest的项目,其中有两个模块的名称分别为app1和app2。(树目录结构如下)

.
├── app1
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── app2
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── db.sqlite3
├── manage.py
└── urlTest
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py


在settings.py中我们可以看到:
ROOT_URLCONF = 'urlTest.urls'
#浏览器访问的所有的url都将在urlTest目录下的urls.py中配置,
urls.py默认加入了admin模块的url:

# urlTest.urls.py 
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

也即是说,每个url都映射到了一个指定的view函数,其中views中定义的函数接受一个request,并返回一个response。
如对view的工作原理不清楚,可参考这里request-response。

这里写代码片
正则表达式与命名组

首先在app1模块中通过正则表达式分别动态的匹配年,年月,年月日类型的URL。
默认的情况下app1模块中是没有urls.py文件,在我们新建了之后,还需要在urlTest的urls.py加上:

url(r'^app1/', include('app1.urls'))
#这样就包括了app1模块的urls.py文件
接下来在新建的app1模块下的urls.py中写动态正则表达式:

#  app1.urls.py
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index),
    url(r'^([0-9]{4})/$', views.pattern1),
    url(r'^([0-9]{4})/(0?[1-9]|1[0-2])/$', views.pattern2),
    url(r'^([0-9]{4})/(0?[1-9]|1[0-2])/(0?[1-9]|[1-2][0-9]|3[0-1])/$', views.pattern3),
    #记得加上^和$否则年月,年月日的匹配都会被年的匹配
]

我用斜杆“/”作为分割年月日的符号,但是为什么斜杆之前要加上圆括号呢?因为当加上圆括号的时候,django就能从URL中捕获这一个值并传递给相对应的views函数,当然使用的是位置传参。
根据URL匹配到指定的views函数后,我分别返回了HttpResponse:

#  app1.views
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.

def index(request):
    return HttpResponse('index', 'text/plain')


def pattern1(request, year):
    return HttpResponse(year, 'text/plain')


def pattern2(request, year, month):
    return HttpResponse(year + month, 'text/plain')


def pattern3(request, year, month, date):
    return HttpResponse(year + month + date, 'text/plain')

刚才我们在使用圆括号进行传参的时候是位置传参,那么如果我们希望使用关键字传参的时候该怎么办呢?
这时候我们就使用到了命名组,命名组的正则表达式语法是(?P<name>pattern),其中name是指传递参数的名字,pattern是指匹配模式。
因此,下面的代码与之前的正则表达式+圆括号是完全等效地:

#  app1.urls.py
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index),
    url(r'^(?P<year>[0-9]{4})/$', views.pattern1),
    url(r'^(?P<year>[0-9]{4})/(?P<month>0?[1-9]|1[0-2])/$', views.pattern2),
    url(r'^(?P<year>[0-9]{4})/(?P<month>0?[1-9]|1[0-2])/(?P<date>0?[1-9]|[1-2][0-9]|3[0-1])/$', views.pattern3),
]

最后值得注意的是在views函数中的参数是可以使用默认参数的,以及可以使用正则表达式进行不捕获参数的设置(可在嵌套参数中使用),如语法(?:….)。
除了捕获URL参数以外,我们还可以直接通过url函数传输额外的数据给view函数。

# urls.py
# 替换index的url
url(r'^$', views.index, {'string': 'Hello World!'})

# views.py
def index(request, string):
    return HttpResponse(string, 'text/plain')

如果在包含include函数的url函数里面传输额外的数据,那么额外的数据将传输给被包含的urls.py的每一行url函数上。
URL模式与命名空间

在url patterns上里面的每一个url函数就是一个URL模式,在django中使用类django.core.urlresolvers.RegexURLPattern来表示。

而url patterns就代表着一个URL分解器(url resolver),使用include函数包含其他的url配置模块也是作为一个URL分解器来解析,在django中使用类django.core.urlresolvers.RegexURLResolver来表示。

命名空间主要分为两种,分别是实例命名空间(instance namespace)以及应用命名空间(application namespace)。

为什么需要命名空间呢?
在之前如果我们通过URL反查的话是通过URL模式中的name属性来进行反查标记的,但是name属性容易重复并且不利于复用,当我们要多次部署一个URL配置模块的时候,就无法通过简单的name属性来进行标记了。

如何设置实例命名空间以及应用命名空间?

# include函数的API
include(arg, namespace=None, app_name=None)
# namespace设置实例命名空间,app_name设置应用命名空间
# 不能只设置app_name,否则会报错,以下是报错的源码
if app_name and not namespace:
    raise ValueError('Must specify a namespace if specifying app_name.')

一般来说,同一应用下的不同实例应该具有相同的应用命名空间,但是,这并不意味着不同应用可以使用相同的实例命名空间,因为实例命名空间在你所有项目中都是唯一的。
URL反向解析


URL反向解析一般是通过reverse函数以及模板中的url标记实现。
我们首先看看在django官方文档中URL反向解析的机制:

    Reversing namespaced URLs
    When given a namespaced URL (e.g. ‘polls:index’) to resolve, Django splits the fully qualified name into parts and then tries the following lookup:

        First, Django looks for a matching application namespace (in this example, ‘polls’). This will yield a list of instances of that application.

        If there is a current application defined, Django finds and returns the URL resolver for that instance. The current application can be specified with the current_app argument to the reverse() function.

        The url template tag uses the namespace of the currently resolved view as the current application in a RequestContext. You can override this default by setting the current application on the request.current_app attribute.

        If there is no current application. Django looks for a default application instance. The default application instance is the instance that has an instance namespace matching the application namespace (in this example, an instance of polls called ‘polls’).

        If there is no default application instance, Django will pick the last deployed instance of the application, whatever its instance name may be.

        If the provided namespace doesn’t match an application namespace in step 1, Django will attempt a direct lookup of the namespace as an instance namespace.

除了最后一个视图名作为name标记来识别,之前的每一个名称首先是作为应用命名空间来识别的(第一条),如果找不到符合的应用命名空间则直接作为实例命名空间来识别(第五条)。
当识别出应用命名空间的时候,再看当前应用有没有定义(即current_app,这里比较容易引起误解,这个当前应用并非应用命名空间,恰恰相反,它是指实例命名空间),如果定义了,直接在之前的已经确认的应用命名空间的所属的实例命名空间列表下寻找current_app的值(第二条)。
如果在实例命名空间列表下找不到current_app的值,那么它会寻找默认的实例命名空间,即名称与应用命名空间相同的实例命名空间。(第三条)
如果连默认的实例命名空间都找不到,那么django会返回最后一个部署的实例命名空间的URL。(第四条)

我们还是通过具体的例子来说明反向解析机制吧。
之前的例子里定义了app1模块和app2模块,再分别生成两个它们的实例。

# urlTest.urls.py
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app2/first/', include('app2.urls', namespace='first', app_name='app2')),
    url(r'^app2/second/', include('app2.urls', namespace='second', app_name='app2')),
    url(r'^app1/third/', include('app1.urls', namespace="third", app_name='app1')),
    url(r'^app1/fourth/', include('app1.urls', namespace="fourth", app_name='app1')),
]

# app1.views.py
from django.core.urlresolvers import reverse
from django.http import HttpResponse
import pdb
def index(request, string):
    pdb.set_trace()
    return HttpResponse(string, 'text/plain')

在pdb的测试环境下我们使用reverse函数分别确定。

(Pdb) reverse('first:index')
u'/app2/first/'
(Pdb) reverse('second:index')
u'/app2/second/'
(Pdb) reverse('third:index')
u'/app1/third/'
(Pdb) reverse('fourth:index')
u'/app1/fourth/'

我们可以看到,实例命名空间能够唯一确定整个项目的URL。

(Pdb) reverse('app1:index')
u'/app1/fourth/'
(Pdb) reverse('app2:index')
u'/app2/second/'

当我们使用应用命名空间的时候,django反向解析机制在没有提供current_app的情况下又找不到默认的实例命名空间,只能返回最后一个部署的实例命名空间。

(Pdb) reverse('app1:index', current_app='third')
u'/app1/third/'
(Pdb) reverse('app1:index', current_app='fourth')
u'/app1/fourth/'
(Pdb) reverse('app2:index', current_app='first')
u'/app2/first/'
(Pdb) reverse('app2:index', current_app='second')
u'/app2/second/'

在提供了实例命名空间之后,即使是使用应用命名空间也能唯一确定URL。
最后我们重新添加默认实例命名空间。

# urlTest.urls.py
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app2/default/', include('app2.urls', namespace='app2', app_name='app2')),
    url(r'^app2/first/', include('app2.urls', namespace='first', app_name='app2')),
    url(r'^app2/second/', include('app2.urls', namespace='second', app_name='app2')),
    url(r'^app1/default/', include('app1.urls', namespace="app1", app_name='app1')),
    url(r'^app1/third/', include('app1.urls', namespace="third", app_name='app1')),
    url(r'^app1/fourth/', include('app1.urls', namespace="fourth", app_name='app1')),
]

# pdb
(Pdb) reverse('app1:index')
u'/app1/default/'
(Pdb) reverse('app2:index')
u'/app2/default/'

最后还要提醒的是,千万不要以为不同应用下可以有相同的实例,namespace必须是unique!

from django.conf.urls import url,include
from django.contrib import admin
from app import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 静态路由
    url(r'^login/',views.login),        # 如 http://127.0.0.1:8080/login
    # 为路由取别名,这样,在地址修改的时候,前端不需要修改地址
    url(r'^register/',views.register,name='reg'),
    # 动态路由,可以用来做分页
    # 无名分组,views里面的函数名参数可任意
    url(r'^article/(\d{4})$',views.article),    # 如 http://127.0.0.1:8080/article/2017
    # 有名分组,view里面的函数名必须是year和month
    url(r'^arcticle/(?P<year>\d{4})/(?P<month>\d{2})',views.arcticle),
    # 路由分发
    url(r'^app/',include('app.urls')),
]


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯