django介绍及路由系统
第一:Python的web框架介绍
Python的WEB框架有Django、Tornado、Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能。
第二:MVC和MTV介绍
MVC介绍:
M:model,模型,是应用程序中用于处理应用程序数据逻辑的部分,主要是负责对数据库进行读写。
V:view,视图,是应用程序中处理数据显示的部分,通常视图是依据模型中的数据创建的。
C:controller,控制器,是应用程序处理与用户交互的部分,通常控制器负责从视图中去读取数据,也向模型去发送数据。
MTV与MVC两者区别与联系:
MTV与MVC这两种模式没有根本上的区别,他们都是让复杂的程序在各个组件之间保持松耦合关系,区别只是名字不同,叫法不同而已。
MTV介绍:
M:Model(模型):负责业务对象与数据库的对象(ORM)
T:Template(模版):负责如何把页面展示给用户
V:View(视图):负责业务逻辑,并在适当的时候调用Model和Template
另外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
第三:django的流程介绍
注:本篇博客采用的是django2.0介绍
django简易流程图:
django简单安装配置
django #安装: pip3 install django 添加环境变量 #1 创建project django-admin startproject mysite ---mysite ---settings.py ---url.py ---wsgi.py ---- manage.py(启动文件) #2 创建APP python mannage.py startapp app01 #3 settings配置 TEMPLATES STATICFILES_DIRS=( os.path.join(BASE_DIR,"statics"), ) STATIC_URL = '/static/' # 我们只能用 STATIC_URL,但STATIC_URL会按着你的STATICFILES_DIRS去找#4 根据需求设计代码 url.py view.py #5 使用模版 render(req,"index.html") #6 启动项目 python manage.py runserver 127.0.0.1:8090 #7 连接数据库,操作数据 model.py
如果用pycharm可以直接建的哦!
目录详细解释:
如果用命令行创建,只会有以下几个目录:
django-admin.py startproject mysite
django-admin.py 是Django的一个用于管理任务的命令行工具,manage.py是对django-admin.py的简单包装,每一个Django Project里都会有一个mannage.py。
manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
urls.py ----- 负责把URL模式映射到应用程序。
用python manage.py startapp app
运行python manage.py runserver 8000 或者直接用pycharm运行
此时,django已经安装成功
现在我们就写一个例子:
mysite的urls:
from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path('app01',include('app01.urls')), ]
app01的urls:
from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ]
app01的views:
from django.shortcuts import render from django.http import HttpResponse def index(request): return HttpResponse("Hello, ylqh.")
访问:
第一个简易的django流程完成
第四:django的url(path)
URL配置(URLconf)就像Django 所支撑网站的目录。是高质量Web应用程序中的一个重要细节,其实质就是通过url指到相应的视图函数
Django可以让你设计URL,没有.php
或没有.cgi
要求。
url()函数可以传递4个参数,其中2个是必须的:正则和view,以及2个可选的参数:kwargs和name
urlpatterns = [
path(正则表达式, views视图函数,参数,别名),
]
参数说明:
1:正则表达式,它是一种匹配字符串或url地址的语法
2:一个可调对象,通常为一个视图函数或者一个指定视图函数的字符串。如果是简单捕获,那么捕获的值将作为一个位置参数进行传递,如果是命名捕获,那么将作为一个关键字参数。
3:可选的任意数量的关键字参数可以作为一个字典传递给目标视图。
4:可选的name参数,对你的URL进行命名,可以让你能够在Django的任意处,尤其是模板内显式地引用它。相当于给URL取了个全局变量名,你只需要修改这个全局变量的值,在整个Django中引用它的地方也将同样获得改变
一:简单示例
django1.x 系列:
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^articles/2018/$', views.special_case_2018), #url(r'^articles/[0-9]{4}/$', views.year_archive), url(r'^articles/([0-9]{4})/$', views.year_archive), #no_named group url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
dgango2.0 系列:
urlpatterns = [ path('index/', views.index), path('articles/2018/', views.special_case_2018), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ]
注意:1:要从url中捕获值,请使用尖括号:<>
2:捕获的值可以选择包含转换器类型。例如:<int : name> 捕获整数参数。
3:没有必要添加一个前斜杠,例如:articles 不是 /articles。
示例请求:
- 请求
/articles/2018/03/
匹配列表中的第三个条目。Django会调用这个函数 。views.month_archive(request, year=2018,month=3)
/articles/2018/
会匹配列表中的第一个模式,而不是第二个模式,因为模式是按顺序传参的,而第一个模式是第一个要传递的参数。在这里,Django会调用这个函数views.special_case_2018(request)
/articles/2018/03/subao/
将匹配最终模式。Django会调用这个函数 。views.article_detail(request, year=2018, month=3, slug="subao")
2.路径转换器:
以下路径转换器默认可用:
str- 匹配任何非空字符串,不包括路径分隔符'/'。默认的转换器不包含在表达式中。 int - 匹配零或任何正整数。返回一个int值法、。 slug - 匹配由ASCII字母或数字组成的字符串,以及加上-字符和下划线字符。例如:subao-yunwei-kaifa。 uuid - 匹配格式化的UUID。为防止多个URL映射到同一页面,必须包含破折号,并且字母必须是小写。例如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID实例。 path- 匹配任何非空字符串,包括路径分隔符 '/'。这使您可以匹配完整的URL路径,而不仅仅是URL路径的一部分字符串。
自定义路径转换器
当默认的Path Converter不能满足需求时,Django2.0支持用户注册自定义的Path Converter。
Path Converter是一个类,定义Converter类需要包含下面的属性或方法:
regex属性,字符串类型。
to_python(self,value),它处理将匹配的字符串转换成应该传递到视图函数的类型。如果它不能转换特定的值,则报ValueError。
to_url(self,value):和 to_python 相反,它会将Python类型转换为在URL中使用的字符串
示例:
app01/urls.py from django.urls import register_converter,path from app01 import views,converters urlpatterns = [ path('articles/<yyyy:year>/', views.year_yyyy), path('articles/<int:year>/', views.year_archive), ]
converters.py class FourDigitYearConverter: regex = '[0-9]{4}' def to_python(self, value): return int(value) def to_url(self, value): return '%04d' % value
views.py def year_yyyy(request,year): return HttpResponse("hello yyyy" + str(year)) def year_archive(request,year): return HttpResponse("hello year" + str(year))
请求示例:
2.正则表达式匹配命名组:
Django2.0也支持我们使用正则表达式来捕获值。注意,用正则表达式捕获值,需要使用re_path(),而不是前面介绍的path()。
正则表达式建议使用命名正则表达式组,语法如下:
(?P<name>pattern)
尖括号里的name为分组名,pattern为正则表达式。
前面的示例可以使用正则表达式修改为:
from django.urls import path, re_path from . import views urlpatterns = [ path('articles/2018/', views.special_case_2018), re_path('articles/(?P<year>[0-9]{4})/', views.year_archive), re_path('articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/', views.month_archive), re_path('articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-_]+)/', views.article_detail), ]
这里与前面的例子的不同点:
1.这里的代码匹配更加严格,比如:year为10001就无法匹配,因为它超出了正则规定的4位数
2.传给view函数的参数为字符串类型,这点和 Django使用url 是一样的
简单介绍未命名的正则表达式组(这种方式不推荐使用!):
除了命名组的语法,例如:(?P<year>[0-9]{4}),相对应的未命名的语法:([0-9]{4})
三.URLconf匹配请求URL中的内容
匹配请求URL中的内容可以分为有名分组和无名分组
3.1无名分组:
urls.py 文件:
from django.contrib import admin from django.urls import include,path,re_path from app01 import views urlpatterns = [ re_path(r'^aritcle/(?:page-(\d+)/)?$', views.blog_articles), #no name group ]
views.py文件
from django.shortcuts import render from django.http import HttpResponse # Create your views here. def blog_articles(request,article_id): return HttpResponse('id is %s context' %article_id)
测试:
3.2 有名参数:
urls文件:
from django.contrib import admin from django.urls import include,path,re_path from app01 import views urlpatterns = [ re_path(r'^aritcle/(?:page-(?P<article_id>\d+)/)?$', views.blog_articles), # name group ]
views.py文件:
from django.shortcuts import render from django.http import HttpResponse # Create your views here. def blog_articles(request,article_id): return HttpResponse('id is %s context' %article_id)
测试:
总结:有名分组和无名分组都是为了获取url中的参数,并传递给视图函数,区别在于无名分组是依据位置参数的形式传递,又名分组是依据关键字的形式传递。
建议:无名分组和又名分组不要混合使用
四.指定视图参数的默认值
一个小技巧是为视图的参数指定默认参数,例子:app01/urls.py
app01/urls.py from django.urls import register_converter,path,re_path from app01 import views,converters urlpatterns = [ path(r'blog', views.page), path(r'blog/page<int:num>/', views.page),#django2.0 url(r'^blog/page(?P<num>[0-9]+)/$', views.page),#django1.x ] views.py def page(request,num='1'): return HttpResponse('hello'+str(num))
在上面的例子中,两个URL模式指向同一个视图views.page
。但是第一个模式不会从URL中捕获任何值。 如果第一个模式匹配,page()函数将使用num参数的默认值"1"。 如果第二个模式匹配,page()将使用捕获的num值
五. 自定义错误页面
当Django找不到与请求匹配的URL时,或者当抛出一个异常时,将调用一个错误处理视图。错误视图包括400、403、404和500,分别表示请求错误、拒绝服务、页面不存在和服务器错误。它们分别位于:
- handler400 —— django.conf.urls.handler400。
- handler403 —— django.conf.urls.handler403。
- handler404 —— django.conf.urls.handler404。
- handler500 —— django.conf.urls.handler500。
注意注意:这些值可以在根URLconf中设置。在其它app01中的二级URLconf中设置这些变量无效
Django有内置的HTML模版,用于返回错误页面给用户,但是这些403,404页面太难看了,通常我们都自定义错误页面。
首先,在根URLconf中额外增加下面的条目:
# URLconf from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/', views.page), url(r'^blog/page<int:num>/', views.page), ] # 增加的条目 handler400 = views.bad_request handler403 = views.permission_denied handler404 = views.page_not_found handler500 = views.page_error
然后在视图中增加:
def page_not_found(request): return render(request, '404.html') def page_error(request): return render(request, '500.html') def permission_denied(request): return render(request, '403.html') def bad_request(request): return render(request, '400.html')
最后,你自己想要啥样的错误页面自己写就ok了。
六. 路由的转发(url中包括其他的urlconf)
一般情况,我们会在每一个app下面,各自建一个urls.py的路由模块,然后从跟路由出发,将app所包含的url请求,全部转发到相应的urls.py模块中,
下面是django本身的URLconf的摘录,它包含需要其他的urlconf:
urlpatterns = [ # ... snip ... path('community/', include('aggregator.urls')), path('contact/', include('contact.urls')), # ... snip ... ]
注意:这个例子中的正则表达式不包含$(字符串结束的匹配符),但是包含一个末尾的斜杠,每当django遇到include()方法的时候,他会截断与该点匹配的URL的部分,并将剩余的字符串发送给include的URLconf以供进一步处理,也就是转发到二级路由里面去。
另外一种转发就是通过path()实例列表来包含url模式。如:
from django.urls import include, path from apps.main import views as main_views from credit import views as credit_views 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)), ]
在这个例子中,/credit/reports/
URL将由credit_views.report()
Django视图处理 。这种做法等同于把二级路由模块内的代码移动到了跟路由模块里面了,不过不推荐这种方式!
看下面的示例1:
from django.urls import path from . import views urlpatterns = [ path('<page_slug>-<page_id>/history/', views.history), path('<page_slug>-<page_id>/edit/', views.edit), path('<page_slug>-<page_id>/discuss/', views.discuss), path('<page_slug>-<page_id>/permissions/', views.permissions), ]
上面的路由,前缀的URLconf中存在冗余的现象,我们可以改进下,只需声明共同的路径(前缀)一次,并将后面的部分转发:
from django.urls import include, path from . import views urlpatterns = [ path('<page_slug>-<page_id>/', include([ path('history/', views.history), path('edit/', views.edit), path('discuss/', views.discuss), path('permissions/', views.permissions), ])), ]
这样看着就舒服多了。
示例2:
当随着业务规模的扩大,app会越来越多,路由也就越来越多,每个app都会有自己的路由,如果仍然把他们全部放到一个路由表中,会很乱,不便于管理,修改,所以我们将app自己的路由交给自己去管理,然后在总路由表中做分发,看如下示例:
我们在mysite下面创建2个app,目录结构如下:
1.创建命令:
创建项目: django-admin startproject mysite 创建应用: python3.5 manage.py startapp app01 python3.5 manage.py startapp app02
2.在每一个app下面创建urls.py 来存放各自的路由,如下:
总的路由urls:
include函数就是分发操作的,当浏览器输入http://127.0.0.1:8001/app01/index的时候,会先进入到总路由表中进行匹配,正则表达式先匹配成功路径app01,然后include功能区app01下的urls中继续匹配剩余部分
from django.contrib import admin from django.urls import include,path,re_path urlpatterns = [ path('admin/', admin.site.urls), path(r'app01/',include('app01.urls')), path(r'app02/',include('app02.urls')), ]
app01下的urls.py 文件
from django.urls import path from . import views urlpatterns = [ path('index', views.index) ]
app01下的views.py文件
from django.http import HttpResponse def index(request): return HttpResponse('<h1>My name is app01 Page</h1>' )
app02下的urls.py文件
from django.urls import path from . import views urlpatterns = [ path('index', views.index) ]
app02下的views.py文件
from django.http import HttpResponse def index(request): return HttpResponse('<h1>My name is app02 Page</h1>' )
测试:
七:嵌套参数
正则表达允许嵌套参数,Django将解析他们并且把它们传递给视图。当反转查询的时候,django将尝试填充所有外部捕获的参数,而忽略任何嵌套捕获的参数,
考虑下面的URL模式,可以选择使用页面参数
from django.urls import re_path urlpatterns = [ re_path(r'^blog/(page-(\d+)/)?$', blog_articles), # bad re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments), # good ]
以上两种方式都是使用了嵌套参数,
# blog/page-1/ 将匹配page-1 并带有两个位置参数式page-1和1
# comments的模式将匹配page_number并带有一个关键字参数是1(这一个例子中外围参数是一个不捕获的参数(?:...))blog_articles
视图需要最外层捕获的参数来反查,在这个例子中是comments或者没有参数,而page-1/
可以不带参数或者用一个page_number
值来反查
八:捕获的参数
包含的URLconf从父URLconf接受捕获的任何参数,官网示例:
# In settings/urls/main.py from django.urls import include, path urlpatterns = [ path('<username>/blog/', include('foo.urls.blog')), ] # In foo/urls/blog.py from django.urls import path from . import views urlpatterns = [ path('', views.blog.index), path('archive/', views.blog.archive), ] 自己示例: #mysite/urls.py from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path('<username>/',include('app01.urls')) ] #app01/urls.py from django.urls import path from app01 import views urlpatterns = [ path('',views.index), path('archive',views.archive), ] #app01/views.py from django.http import HttpResponse def index(request,username): return HttpResponse("Hello, %s" % username) def archive(request,username): return HttpResponse("Hello, %s " % username)
上面的两种url都可以捕获到变量,并传递给urlconf,最终传给视图
九:传递额外的参数给视图
URLconf具有一个钩子(hook),允许你传递一个Python字典作为额外的关键字参数给视图函数
from django.urls import path from . import views urlpatterns = [ path('blog/<int:year>/', views.year_archive, {'foo': 'bar'}), ]
在上面的例子中,对于/blog/2005/
请求,Django将调用views.year_archive(request, year='2005', foo='bar')
。理论上,你可以在这个字典里传递任何你想要的传递的东西。但是要注意,URL模式捕获的命名关键字参数和在字典中传递的额外参数有可能具有相同的名称,这会发生冲突,要避免。
#mysite/urls.py from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path('app01/',include('app01.urls')), ] #app01/urls.py from django.urls import register_converter,path,re_path from app01 import views,converters urlpatterns = [ path('<int:year>/',views.year_archive,{'foo':'bar'}) ] #app01/views.py from django.http import HttpResponse def year_archive(request,year,foo='bar'): print(year,foo) return HttpResponse("hello year")
十:传递额外的参数给include()
类似上面,也可以传递额外的参数给include()。参数会传递给include指向的urlconf中的每一行
例如:下面的两个URLconf在功能上是相同的:
第一:官网配置: # main.py from django.urls import include, path urlpatterns = [ path('blog/', include('inner'), {'blog_id': 3}), ] # inner.py from django.urls import path from mysite import views urlpatterns = [ path('archive/', views.archive), path('about/', views.about), ] 自己验证: #mysite/urls.py from django.urls import include,path urlpatterns = [ path(r'blog/', include('app01.urls'),{'blog_id':3}), ] #app01/urls.py from django.urls import path from app01 import views urlpatterns = [ path('archive/',views.archive), path('about/',views.about), ] #app01/views.py from django.http import HttpResponse def archive(request,blog_id): return HttpResponse("Hello archive %s" % blog_id) def about(request,blog_id): return HttpResponse('hello about %s' % blog_id)
第二:
官网配置: # main.py from django.urls import include, path from mysite import views urlpatterns = [ path('blog/', include('inner')), ] # inner.py from django.urls import path urlpatterns = [ path('archive/', views.archive, {'blog_id': 3}), path('about/', views.about, {'blog_id': 3}), ] 自己验证: #mysite/urls.py from django.urls import include,path urlpatterns = [ path(r'blog/', include('app01.urls')), ] #app01/urls.py from django.urls import path from app01 import views urlpatterns = [ path('archive/',views.archive,{'blog_id':3}), path('about/',views.about,{'blog_id':3}), ] #app01/views.py from django.http import HttpResponse def archive(request,blog_id): return HttpResponse("Hello archive %s" % blog_id) def about(request,blog_id): return HttpResponse('hello about %s' % blog_id)
注意,只有当你确定被include的URLconf中的每个视图都接收你传递给它们的额外的参数时才有意义,否则其中一个以上视图不接收该参数都将导致错误异常。
十一:URL反向解析
可获取URL的主要信息负责处理他的视图的标识(例如名称),其他必须参与查找正确URL的信息,包括视图参数的类型(位置,关键字)和值
django提供了一种解决方案,使得URL映射器是URL设计的唯一存储库,用URLconf提供它,可以在两个方向上使用:
1.从用户/浏览器请求的URL开始,它调用正确的django视图,提供它可能需要的任何参数以及从URL中提取的值,
2.从相应的django视图的标识以及将传递给他的参数的值开始获取管理的URL
第二种就是所谓的URL方向解析,方向URL匹配,方向URL查找或者仅仅URL反转。
Django提供了用于执行URL反转的工具,以匹配需要URL的不同层:
1.在模板中:使用URL模板标签。(也就是写前端网页时)
2.在python代码中:使用reverse()
函数。(也就是views中)
3.在更高层的与处理Django模型实例相关的代码中:使用get_absolute_url()
方法。(也就是在模型model中)
例子:URLconf
from django.urls import path from . import views urlpatterns = [ #... path('articles/<int:year>/', views.year_archive, name='news-year-archive'), #... ]
根据这个设计,对应与年份nnnn的档案URL是/articles/nnnn
您可以使用下面的方式在模板中获得这些内容:
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a> {# Or with the year in a template context variable: #} <ul> {% for yearvar in year_list %} <li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li> {% endfor %} </ul>
或者在python代码中:
from django.urls import reverse from django.http import HttpResponseRedirect def redirect_to_year(request): # ... year = 2006 # ... return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
如果出于某种原因决定应该更改每年发布文章内容的URL,那么您只需要更改URLconf中的条目。
在某种情况下,视图是具有通用性的,因此URL和视图之间可能存在多对一的关系,对于这些情况,视图名称在倒转URL时不是一个足够好的标识符。见下面的命名URL模式
URL模式:
#mysite/urls.py from django.urls import include,path urlpatterns = [ path(r'blog/', include('app01.urls')), ] #app01/urls.py from django.urls import path from app01 import views urlpatterns = [ path('art/',views.art,name='app01-art'), ] #app01/views.py from django.shortcuts import render def art(request): return render(request,'art.html',{"day":"abc"}) #art.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>{{ day }}</h1> <a href="{% url 'app01-art' %}">点击</a> </body> </html>
视图:在视图函数中调用url,使用reverse('别名')
####urls from django.conf.urls import url,include from app01 import views urlpatterns = [ url(r'^art/', views.art3, name='app01-art'), ] ####views def art3(request): print(reverse('app01-art')) #在视图函数中对url进行反向解析 return HttpResponse("OK")
案例:登陆成功跳转打index.html
urls.py文件
from django.contrib import admin from django.urls import include,path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r'login/',views.login,name='login_page'), #路径login/的别名为login_page path(r'index/',views.index,name='index_page'), # 路径index/的别名为index_page ]
在views.py中
from django.shortcuts import render from django.http import HttpResponse from django.shortcuts import reverse #反向解析 from django.shortcuts import redirect #用于重定向 import datetime # Create your views here. def login(request): if request.method == 'GET': #如果是get请求,返回login.html页面,页面中的{% url ‘login_page’ %}会被反向解析成路径:/login return render(request, 'login.html') elif request.method == 'POST': 如果是post请求,可以从request.post中取出请求体的数据 name = request.POST.get('name') pwd = request.POST.get('pwd') if name == 'Kevin' and pwd == '123': url = reverse('index_page') #reverse会将别名’index_page' 反向解析成路径:/index/ return redirect(url) #重定向到/index/ else: return HttpResponse('username or pwd is error') def index(request): return render(request,'index.html')
login.html页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>login page</title> </head>> <body> <!--login_page 必须加引号--> <form action="{% url 'login_page' %}" method="post"> {% csrf_token %} <!---必须加上--> <p>username: <input type="text" name="name"></p> <p>password: <input type="password" name="pwd"></p> <p><input type="submit" value="submit"></p> </form> </body>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head>> <body> <h3>my name is index page</h3> </body> </html>
测试:
访问http://127.0.0.1:8001/login/会到登陆页面,用户名密码正确会跳转到index.html,不正确会返回错误提示
总结:
在view.py中,反向解析的使用: url = reverse('index_page') 在模板login.html文件中,反向解析的使用: {% url 'login_page' %}
add 1:反向解析与无名分组
eg: urls.py文件:
from django.contrib import admin from django.urls import include,path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), re_path(r'^article/(?:page-(\d+)/)?$', views.articles,name='article_page'), #无名分组,articles的别名为:article_page。 re_path(r'^index_page/(?:page-(\d+)/)?$', views.articles_index,name='index_page'), #无名分组,articles_index的别名为: index_page ]
views.py文件:
from django.shortcuts import render from django.http import HttpResponse from django.shortcuts import reverse from django.shortcuts import redirect import datetime # Create your views here. def articles_index(request,page_id): print(page_id) if request.method == 'GET': return render(request, 'login.html') name = request.POST.get('name') pwd = request.POST.get('pwd') if name == 'zzl' and pwd == '123': url = reverse('article_page', args=(page_id,)) #反向解析到/article/page-2/ return redirect(url) else: return HttpResponse('username or pwd is error') def articles(request,page_id): return render(request,'index.html')
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>login page</title> </head> <body> <form action="{% url 'article_page' 1 %}" method="post"> <!--反向解析到/article/page-2/--> {% csrf_token %} <p>username: <input type="text" name="name"></p> <p>password: <input type="password" name="pwd"></p> <p><input type="submit" value="submit"></p> </form> </body> </html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>hello index</h1>>
<h3><a href="{% url 'index_page' 2 %}">url</a>></h3> <!-- 反向解析到/index_page/page-2 -->
</body>
</html>
测试:
访问http://172.0.0.1:8001/index_page/page-2/ 输入用户名密码,点击submit,将会跳转到http://127.0.0.1:8001/article/page-1/,
点击url连接地址,将会再次跳转到htpp://127.0.0.1:8001/index_page/page-2/
add 2:反向解析与有名分组
urls.py文件
from django.contrib import admin from django.urls import include,path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), re_path(r'^aritcle/(?:page-(?P<page>\d+)/)?$', views.articles,name='article_page'), # 有名分组,articles的别名为article_page re_path(r'^index_page/(?:page-(?P<page>\d+)/)?$', views.articles_index,name='index_page'), # name group,articles_index的别名为index_page ]
views.py文件
from django.shortcuts import render from django.http import HttpResponse from django.shortcuts import reverse from django.shortcuts import redirect import datetime # Create your views here. def articles_index(request,page): if request.method == 'GET': return render(request, 'login.html') name = request.POST.get('name') pwd = request.POST.get('pwd') if name == 'zzl' and pwd == '123': url = reverse('article_page', kwargs={'page':page}) #反向解析到/article/page-2/ return redirect(url) else: return HttpResponse('username or pwd is error') def articles(request,page): return render(request,'index.html')
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>login page</title> </head> <body> <form action="{% url 'article_page' page=1 %}" method="post"> <!--反向解析到/article/page-2/--> {% csrf_token %} <p>username: <input type="text" name="name"></p> <p>password: <input type="password" name="pwd"></p> <p><input type="submit" value="submit"></p> </form> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>hello index</h1> <h3><a href="{% url 'index_page' page=3 %}">url</a>></h3> </body> </html>
测试:
访问http://172.0.0.1:8001/index_page/page-2/ 输入用户名密码,点击submit,将会跳转到http://127.0.0.1:8001/article/page-1/,
点击url连接地址,将会再次跳转到htpp://127.0.0.1:8001/index_page/page-2/
十二:URL命名空间
URL命名空间可以保证反查到唯一的URL,即使不同的app使用相同的URL名称。
第三方应用始终使用带命名空间的URL是一个很好的做法。
类似地,它还允许你在一个应用有多个实例部署的情况下反查URL。 换句话讲,因为一个应用的多个实例共享相同的命名URL,命名空间提供了一种区分这些命名URL 的方法。
实现命名空间的做法很简单,在urlconf文件中添加app_name = 'polls'
和namespace='author-polls'
这种类似的定义
例子:
urls.py from django.urls import include, path urlpatterns = [ path('author-polls/', include('polls.urls', namespace='author-polls')), path('publisher-polls/', include('polls.urls', namespace='publisher-polls')), ]
polls/urls.py from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('<int:pk>/', views.DetailView.as_view(), name='detail'), ... ]
视图中的调用方法:
reverse('polls:index', current_app=self.request.resolver_match.namespace)
模板中:
{% url 'polls:index' %}
示例区分:
当我们创建了2个或者2个以上的app的时候,并且每个app下都针对匹配的路径起了别名,如果别人重复,那么在反向解析的时候则会出现一下情况:
1.总的urls.py
from django.contrib import admin from django.urls import include,path,re_path urlpatterns = [ path('admin/', admin.site.urls), path(r'app01/', include('app01.urls')), path(r'app02/', include('app02.urls')), ]
2.app01下的urls.py
from django.urls import path from . import views urlpatterns = [ #为匹配的路径app01/index/ 起别名 'index_page' path(r'index/', views.index, name='index_page'), ]
3.app02下的urls.py
from django.urls import path from . import views urlpatterns = [ #为匹配的路径app02/index/起别名'index_page',与app01中的别名相同 path(r'index/', views.index, name='index_page'), ]
3.app01下的views.py
from django.shortcuts import render from django.http import HttpResponse from django.shortcuts import reverse from django.shortcuts import redirect # Create your views here. def index(request): url = reverse('index_page') return HttpResponse('app01的index页面,反向解析结果为%s' %url)
4.app02下的views.py
from django.shortcuts import render from django.http import HttpResponse from django.shortcuts import reverse from django.shortcuts import redirect # Create your views here. def index(request): url = reverse('index_page') return HttpResponse('app02的index页面,反向解析结果为%s' %url)
测试:
通过测试发现,无论访问app01/index还是app02/index ,反向解析的结果都是/app02/index,解决这个问题之一就是避免使用相同的别名,那就需要用django中名称空间的概念,将别名放到不同的名称空间中,这样即便事出现重复,彼此也不会冲突,具体做法如下:
1.在总的urls.py路由转发的时候,指定名称空间,如下:
from django.contrib import admin from django.urls import include,path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), #传给include功能一个元组,元组的第一个值是路由分发的地址,第二个值则是我们为名称空间起的名字 path(r'app01/', include(('app01.urls','app01'))), path(r'app02/', include(('app02.urls','app02'))), ]
2.app01下的urls.py
from django.urls import path from . import views urlpatterns = [ #为匹配的路径app01/index/ 起别名 'index_page' path(r'index/', views.index, name='index_page'), ]
3.app02下的urls.py
from django.urls import path from . import views urlpatterns = [ #为匹配的路径app02/index/起别名'index_page',与app01中的别名相同 path(r'index/', views.index, name='index_page'), ]
4.app01下的views.py
from django.shortcuts import render from django.http import HttpResponse from django.shortcuts import reverse from django.shortcuts import redirect import datetime # Create your views here. def index(request): url = reverse('app01:index_page') return HttpResponse('app01的index页面,反向解析结果为%s' %url)
5.app02下的views.py
from django.shortcuts import render from django.http import HttpResponse from django.shortcuts import reverse from django.shortcuts import redirect # Create your views here. def index(request): url = reverse('app02:index_page') return HttpResponse('app02的index页面,反向解析结果为%s' %url)
测试:
补充:
1. 在视图函数中基于名称空间的反向解析,用法如下: url=reverse('名称空间的名字:待解析的别名') 2. 在模板里基于名称空间的反向解析,用法如下: <a href="{% url '名称空间的名字:待解析的别名' %}">hello django<>