Django基础-url控制器
MTV模型
- Model: 和数据库相关, 负责业务对象和数据库对象(ORM)
- Template: 放所有的html文件, 通过模板语法将数据库当中的数据渲染到页面中, 以便在前端页面展示
- View: 负责业务逻辑, 并在适当的时候调用
Model
和Template
此外,Django还有一个urls分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
Django基础命令
1. 下载Django
pip install django==2.0.1 # 本文使用的django版本为2.0.1
2. 创建一个Django project
django-admin.py startproject mysite
会生成如下目录结构
- manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
- urls.py ----- 负责把URL模式映射到应用程序。
3. 进入project目录创建一个应用
python3 manage.py startapp blog(应用名称)
4. 启动Django项目
python3 manage.py runserver 8080
5. 配置静态文件目录
修改项目的settings.py, 加入STATICFILES_DIRS
部分的代码
STATIC_URL = '/static/' # 静态文件路径别名, django对引用名和实际名进行映射,引用时,只能按照引用名来, # 不能按实际名去找
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'statics') # statics为实际目录, 这个目录的名字可以自定义
]
路由控制和分发(include)
项目全局urls.py文件
from django.urls import path,re_path,include # re_path通过正则匹配路径, include可以把请求分发到不同的app下的 # url分发器
from app01 import views # 导入应用app01的视图文件
urlpatterns = [
# path('admin/', admin.site.urls),
# path('timer/', views.timer),
# 路径(timer/)----------->视图函数(views.timer)
re_path(r'^articles/([0-9]{4})',views.articles), # 会给articles这个视图传入request和([0-9]{4})两个参数, 所 # 以articles这个视图需要接收两个参数
path('login/', views.login),
re_path(r'^',include('app01.urls'))
]
# 1 一旦匹配成功则不再继续
# 2 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
# 3 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
# 4 每个正则表达式前面的'r' 是可选的但是建议加上。
app01下的urls, 由上一段代码的include
分发到这里
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
re_path(r'^articles/([0-9]{4})',views.articles)
]
有名分组
在更高级的用法中,可以使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。
在Python 正则表达式中,命名正则表达式组的语法是(?P<name>pattern)
,其中name
是组的名称,pattern
是要匹配的模式
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。例如:
/articles/2005/03/
请求将调用views.month_archive(request, year='2005', month='03')函数
/articles/2003/03/03/
请求将调用函数views.article_detail(request, year='2003', month='03', day='03')。
在实际应用中,这意味你的URLconf 会更加明晰且不容易产生参数顺序问题的错误 —— 你可以在你的视图函数定义中重新安排参数的顺序
反向解析
在使用Django项目时, 一个常见的需求是获取url的最终形势, 以嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。
如果通过硬编码写死到代码中会产生很多问题, 影响代码的扩展性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="{% url 'Log' %}" method="post">
name: <input type="text" name="user">
pass: <input type="password" name="pwd">
<input type="submit">
</form>
</body>
</html>
# urls.py
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
path('login.html/', views.login, name='Log')
]
在urlpatterns
中给path起一个别名name='Log'
, 然后在html文件中引用<form action="{% url 'Log' %}" method="post">
from django.shortcuts import render,HttpResponse
from django.urls import reverse
# Create your views here.
def timer(request):
import time
ctime=time.time()
url=reverse('Log')
print(url)
return render(request,"timer.html",{'ctime':ctime})
在django中需要引入reverse方法来实现反向解析
如果urls.py中的路径是带有正则表达式的, 则reverse的时候就要给一个参数, 只要这个参数符合正则表达式的匹配规则即可
# urls.py
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
re_path(r'^articles/([0-9]{4})',views.articles,name='articles_reverse')
]
# views.py
from django.shortcuts import render,HttpResponse
from django.urls import reverse
# Create your views here.
def timer(request):
import time
ctime=time.time()
url=reverse('articles_reverse',args=(1111,)) # 给出一个四位数字的元组即可
print(url)
return render(request,"timer.html",{'ctime':ctime})
名称空间(namespace)
命名空间(英语:Namespace)是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其它命名空间中。
由于name没有作用域,Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,立即返回
我们在开发项目时,会经常使用name属性反解出URL,当不小心在不同的app的urls中定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入了命名空间。
没有定义namespace的情况
项目urls.py分发器
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
from app02 import views
urlpatterns = [
re_path(r'^app01/', include('app01.urls')),
re_path(r'^app02/', include('app02.urls')),
]
app01的urls.py分发器
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
re_path(r'^index/', views.index,name="index"),
]
app02的urls分发器
from django.contrib import admin
from django.urls import path,re_path,include
from app02 import views
urlpatterns = [
re_path(r'^index/', views.index,name="index"),
]
app01的views.py
from django.shortcuts import render,HttpResponse
from django.urls import reverse
def index(request):
return HttpResponse(reverse("index"))
app02的views.py
from django.shortcuts import render,HttpResponse
from django.urls import reverse
def index(request):
return HttpResponse(reverse("index"))
按照以上配置, 没有namespace的情况下, 在两个app同时定义了index这个访问路径, django就没有办法区分用户要访问的是那个index了, 所以他只能按照优先顺序, 返回第一个匹配到的index, 在这里访问以下链接得到的结果都是/app02/index/
定义namespace的情况
项目urls.py分发器
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
from app02 import views
urlpatterns = [
re_path(r'^app01/', include(("app01.urls","app01"))), #("app01.urls","app01") 这是一个元组
re_path(r'^app02/', include(("app02.urls","app02"))),
]
app01的urls.py分发器
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
re_path(r'^index/', views.index,name="index"),
]
app02的urls分发器
from django.contrib import admin
from django.urls import path,re_path,include
from app02 import views
urlpatterns = [
re_path(r'^index/', views.index,name="index"),
]
app01的views.py
from django.shortcuts import render,HttpResponse
from django.urls import reverse
def index(request):
return HttpResponse(reverse("app01:index"))
app02的views.py
from django.shortcuts import render,HttpResponse
from django.urls import reverse
def index(request):
return HttpResponse(reverse("app02:index"))
按照以上配置, 定义了namespace的情况下, 在两个app同时定义了index这个访问路径, django就可以通过namespace区分用户要访问的是那个index了, 在这里访问以下链接得到的结果分别是/app01/index/
和/app02/index/
-
http://127.0.0.1:8080/app01/index/
返回/app01/index/
-
http://127.0.0.1:8080/app02/index/
返回/app02/index/
path方法
path转换器
如下代码, 所有传入视图的参数默认均为字符串类型, 如果我们需要处理的数据不是字符串类型, 我们就要在视图函数中自行处理, 比如第一个year
, 如果视图函数中需要的类型是整形, 我们就要在视图函数中做一个类型转换year=int('year')
, 那么有没有一种方法,在url中,使得这一转化步骤可以由Django自动完成?
urlpatterns = [
re_path('articles/(?P<year>[0-9]{4})/', year_archive),
re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view),
]
通过使用path转换器可以解决上述问题, 代码如下
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug>/', views.article_detail),
]
基本规则
- 使用尖括号(
<>
)从url中捕获值。 - 捕获值中可以包含一个转化器类型(converter type),比如使用
<int:name>
捕获一个整数变量。若果没有转化器,将匹配任何字符串,当然也包括了/
字符。 - 无需添加前导斜杠。
自定义path转换器
对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:
-
regex
类属性,字符串类型 -
to_python(self, value)
方法,value是由类属性regex
所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。 -
to_url(self, value)
方法,和to_python
相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。
示例
自定义转换器urlconvert.py
, 转换器名字自定义, 代码中的regex, to_python, to_url
为固定写法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class MonConvert:
regex='[0-9]{2}'
def to_python(self,value):
return int(value)
def to_url(self,value):
return '%04d' %value
通过register_converter
模块注册我们自定义的转换器MonConvert
from django.urls import path,re_path,include,register_converter
from app01 import views
from app02 import views
from app01.urlconvert import MonConvert
register_converter(MonConvert,'mm') # mm为我们给自定义转换器起的别名
urlpatterns = [
path('articles/<mm:mon>',views.articles),
]
view.articles代码
from django.shortcuts import render,HttpResponse
from django.urls import reverse
def articles(request,mon):
return HttpResponse(mon)
当我们在浏览器输入127.0.0.1:8000/articles/22, 就会在web页面返回22了, 并且代码中获得的mon的字符类型为int类型