路由层
url
定义
URL即统一资源定位符,网络资源的地址,包含对应文件的位置和浏览器对该资源的处理方式。
格式
schema://host[:port#]/path/.../[?query-string][#anchor]
# 栗子:http://zdq.com/index/
**schema:**协议,如http,https,ftp等;
**host:**地址或域名;
**port:**端口号,http默认端口80;
**path:**资源路径,用于匹配需要访问的资源;
**query-string:**向服务器发送的数据参数;
**anchor:**锚点。
urls.py
url加载是从配置文件rurls.py开始的,django项目中,urls.py文件是一张映射表,将项目中声明的url对应的视图函数建立映射关系.
网页向后端发送想要访问的接口,只有输入url的路径与配置文件中路径参数一致时,后端通过urls路由层来找到前端需求对应的接口.
路由匹配
settings中参数默认APPEND_SLASH = True,可手动设置为False
作用是django会自动给传到后端的url后加/
第一步,先用原url寻找,
第二步,如果找不到,url后加/
再寻找
第三步,如果害是找不到,404NotFound
匹配规则
url后第一个参数是正则表达式,将前端传来的url当作字符串进行匹配,如匹配成功,立刻返回,
所以建议再url中的函数名后加/
urlpatterns = [
url(r'^admin/', admin.site.urls), # url第一个参数是一个正则表达式
url(r'^test/$', views.test), # 一旦正则表达式能够匹配到内容 会立刻结束匹配关系 直接执行后面对应的函数
url(r'^testadd/$', views.testadd),#^标识开头,$标识结尾
]
# django匹配路由的规律
# 不加斜杠 先匹配一次试试 如果匹配不上 会让浏览器重定向 加一个斜杠再来一次 如果还匹配不上 才会报错
注意:
路由匹配值匹配url部分
不匹配?后面的get携带的参数
无名分组
将分组内正则表达式匹配到的内容当作位置参数传递给视图函数.
url(r'^wuminggroup/([0-9]{4})/([0-9]{4})/', views.wuminggroup),
--------------------------------
def wuminggroup(request,*args):
for i in args:
print(i)
# print(args)
return HttpResponse('200 ok')
有名分组
会将分组内的正则表达式匹配到的内容当作关键字参数传递给视图函数
url(r'^youminggroup/(?P<kwargs>d+/)', views.youminggroup),
--------------------------------
def youminggroup(request,kwargs):
print(kwargs)
return HttpResponse("kwargs")
利用有名和无名分组,我们就可可以再调用视图函数之前给函数传递额外的参数.
注意:
-
无名和有名分组不能同时使用,
但是同一种分组的情况下,同一种类可以使用多次(无名* n 或者 有名 * n)
-
无名可以使用
*args
接收,有名必须给定key值.
反向解析
根据一个别名,动态解析出一个结果,该结果可以直接访问对应的url
情况1:路由中没有正则
写死的
#urls.py
url(r'^home/', views.home,name='xxx'), # 给路由与视图函数对应关系起别名
前端反向解析
#html
<p><a href="{% url "xxx"%}">111</a></p>
后端反向解析
#views
from django.shortcuts import render,HttpResponse,redirect,reverse
def get_url(request):
url = reverse('xxx')
print(url)
return HttpResponse("get_url")
情况二:无名分组的反向解析
在解析时,需要手动指定正则匹配的内容
#urls.py
url(r'^home/(d+)/', views.home,name='xxx'), # 给路由与视图函数对应关系起别名
前端反向解析
#html
<p><a href="{% url "xxx" user_obj.id %}">111</a></p>
后端反向解析
#views
def get_url(request):
url = reverse('xxx',args=(1,))
print(url)
return HttpResponse('get_url')
手动传入参数,需要满足能够被正则表达式匹配的到.
情况三:有名分组反向解析
在解析时,需要手动指定正则匹配的内容
有名分组的反向解析可以与无名分组的一样
但是,最最正规的写法:
url(r'^home/(?P<year>d+)/', views.home,name='xxx'),
# 给路由与视图函数对应关系起别名
前端反向解析
# 可以直接用无名分组的情况
<p><a href="{% url 'xxx' 12 %}">111</a></p>
# 你也可以规范写法
<p><a href="{% url 'xxx' year=1232 %}">111</a></p> # 了解即可
后端反向解析
# 可以直接用无名分组的情况
url = reverse('xxx',args=(1,))
# 你也可以规范写法
url = reverse('xxx',kwargs={'year':213123}) # 了解即可
小例子:
url(r'^edit_user/(d+)/',views.edit_user,name='edit')
def edit_user(request,edit_id):
# edit_id就是用户想要编辑数据主键值
pass
{% for user_obj in user_list %}
<a href='/edit_user/{{user_obj.id}}/'>编辑</a> 无名分组
<a href='{% url 'edit' user_obj.id %}'>编辑</a> 反向解析
{% endfor %}
PS:这个后端反向解析,除了集体改名方便,也不知道还有啥用
路由分发
为了使多个app同时开发互相不干扰,
路由分发就是为了解决项目的总路由匹配关系过多的情况
每个app下手动创建urls.py存放自己的路由
app01下的urls.py
from django.conf.urls import url
# 导入app01的views
from app01 import views
urlpatterns = [
url(r'^index/$',views.index),
]
app01下的views.py
from django.shortcuts import render
from django.shortcuts import HttpResponse
def index(request):
return HttpResponse('我是app01的index页面...')
app02下的urls.py
from django.conf.urls import url
# 导入app02的views
from app02 import views
urlpatterns = [
url(r'^index/$',views.index),
]
app02下的views.py
from django.shortcuts import render
from django.shortcuts import HttpResponse
def index(request):
return HttpResponse('我是app02的index页面...')
总urls.py文件
from django.conf.urls import url,include
from django.contrib import admin
# 总路由表
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 新增两条路由,注意不能以$结尾
# include函数就是做分发操作的,当在浏览器输入http://127.0.0.1:8001/app01/index/时,会先进入到总路由表中进行匹配,正则表达式r'^app01/'会先匹配成功路径app01/,然后include功能会去app01下的urls.py中继续匹配剩余的路径部分
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
]
测试:
python manage.py runserver 8000
# 在浏览器输入:http://127.0.0.1:8000/app01/index/ 会看到:我是app01的index页面...
# 在浏览器输入:http://127.0.0.1:8000/app02/index/ 会看到:我是app02的index页面...
名称空间
当多个app同时存在,且每个app下都对匹配的路径起了别名,如果别名重复,在反向解析时i会出现覆盖
为了解决这个问题,如果像使用相同的别名,就需要将别名放到不同的名称空间中,这样即使重复也不会冲突,
在总路由urls.py分发时,指定名称空间
from django.conf.urls import url, include
from django.contrib import admin
# 总路由表
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 传给include功能一个元组,元组的第一个值是路由分发的地址,第二个值则是我们为名称空间起的名字
url(r'^app01/', include(('app01.urls','app01'))),
url(r'^app02/', include(('app02.urls','app02'))),
]
修改每个app下的view.py中视图函数,针对不同名称空间中的别名'index_page'做反向解析
app01下的views.py
from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.shortcuts import reverse
def index(request):
url=reverse('app01:index_page') # 解析的是名称空间app01下的别名'index_page'
return HttpResponse('app01的index页面,反向解析结果为%s' %url)
app02下的views.py
from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.shortcuts import reverse
def index(request):
url=reverse('app02:index_page') # 解析的是名称空间app02下的别名'index_page'
return HttpResponse('app02的index页面,反向解析结果为%s' %url)
总结:
1、在视图函数中基于名称空间的反向解析,用法如下
url=reverse('名称空间的名字:待解析的别名')
2、在模版里基于名称空间的反向解析,用法如下
<a href="{% url '名称空间的名字:待解析的别名'%}">哈哈</a>
使用app名加url名作为别名,永不重复!
伪静态
在url最后加上.html
,将一个动态网页伪装成动态网页,以此来增加搜索引擎SEO查询频率和收藏力度
虚拟环境
相当于每创建一个虚拟环境就重新下载了一个纯净的python解释器
django版本区别
django1.X django2.X
urls.py中路由匹配的方法有区别
django2.X用的是path
urlpatterns = [
path('admin/', admin.site.urls),
]
django1.X用的是url
urlpatterns = [
url(r'^reg.html',views.reg,name='app02_reg')
]
# 区别 django2.X里面path第一个参数不是正则也不支持正则 写什么就匹配什么
# 虽然path不支持正则 感觉也好用 django2.X还有一个re_path的方法 该方法就是你django1.X里面url
# 虽然path支持 但是它提供了五种转换器 能够将匹配到的数据自动转黄成对应的类型
# 除了有默认五种转换器之外 还支持你自定义转换器
1、path与re_path或者1.0中的url的不同之处是,传给path的第一个参数不再是正则表达式,而是一个完全匹配的路径,相同之处是第一个参数中的匹配字符均无需加前导斜杠
2、使用尖括号(<>)从url中捕获值,相当于有名分组
3、<>中可以包含一个转化器类型(converter type),比如使用 <int:name> 使用了转换器int。若果没有转化器,将匹配任何字符串,当然也包括了 / 字符
五种默认的转换器
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
自定义转换器
-
在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中,使用
register_converter
将其注册到URL配置中:from django.urls import path,register_converter from app01.path_converts import MonthConverter register_converter(MonthConverter,'mon') from app01 import views urlpatterns = [ path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'), ]
-
views.py中的视图函数article_detail
from django.shortcuts import render,HttpResponse,reverse def article_detail(request,year,month,other): print(year,type(year)) print(month,type(month)) print(other,type(other)) print(reverse('xxx',args=(1988,12,'hello'))) # 反向解析结果/articles/1988/12/hello/ return HttpResponse('xxxx')
-
测试
# 1、在浏览器输入http://127.0.0.1:8000/articles/2009/12/hello/,path会成功匹配出参数year=2009,month=12,other='hello'传递给函数article_detail # 2、在浏览器输入http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配失败,因为我们自定义的转换器mon只匹配两位数字,而对应位置的123超过了2位
自定义起始页面和404页面
主页
def home(request):
return HttpResponse('home主页')
# 网站首页
url(r'^$',home),
404页面
def error(request):
return HttpResponse('404页面')
# 网站404页面的设计
url(r'',error)