文章目录
- 一、视图的介绍
- 1.视图是什么?
- 2.视图模板的配置
- 3.视图函数的使用
- 二. URL映射
- 1.URL路由分发
- 2.URL反向解析
- 3.URL正则路径
- 4.URL命名空间
一、视图的介绍
1.视图是什么?
视图函数(或简称视图)只是一个Python函数,它接受Web请求并返回Web响应。此响应可以是网页的HTML内容,重定向,404错误,XML文档或图像。。。
无论视图本身包含什么逻辑,都要返回响应
。代码写在哪里都可以,只要在 Python 目录下面,一般放在项目的 views.py 文件中。
每个视图函数都负责返回一个 HttpResponse 对象,对象中包含生成的响应。视图层中有两个重要的对象:请求对象(request)与响应对象(HttpResponse)。
2.视图模板的配置
在创建一个模板时,我们需要在指定的文件夹下存放html模板时,都必须存放在template目录下,当然这是可以修改的,而修改需要在主目录\settings.py配置文件中修改,如下:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')] # 这里修改文件名
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
前面也提过了静态文件的存放目录相关配置,这里在回顾一下,如下:
STATIC_URL = '/static/'
# 配置静态文件的名字,这里写的和上面同名,以后的静态文件就可以存放在当前目录下了
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
视图定义好后,要与URL进行映射,从而保证用户在浏览器中输入指定url可以请求到这个视图函数。 在输入了某个url、请求网站时,django会从项目的urls.py文件中寻找对应的视图,这在settings.py中已经进行配置如下:
ROOT_URLCONF = 'untitled4.urls'
在urls.py文件中有一个urlpatterns变量,django会从这个变量中读取所有的匹配规则。 匹配规则需要使用
django.urls.path
或
django.urls.url
函数进行包裹,这个函数会根据传入的参数返回URLPattern或者是URLResolver的对象,如下:
from app01 import views
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
]
3.视图函数的使用
前面已经提到过,视图一般都在app的views.py中定义,并且视图的第一个参数必须是request(一个HttpRequest对象,准确来说是一个
django.core.handlers.wsgi.WSGIRequest
对象),同时返回结果必须是
HttpResponseBase对象或者子类的对象。
视图一般用于完成一些业务逻辑,在Django中一般用于和前后端响应,交互,来达成前后端不分离效果。
响应对象主要有三种形式:
- 一.HttpResponse
- 1.Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个
HttpRequest 对象传给视图模板
。 - 2.视图函数在处理完相关的逻辑后,也需要返回一个响应给浏览器。而这个响应,
可以是一个字符串或一个html标签...
- 3.而 HttpResponse 则是 HttpResponseBase 用得最多的子类。
- 二.render
- 1.同HttpResponseBase一样同样需要上传reuqest对象
- 2.返回一个视图模板
- 3.(可选)返回一个字典传到里面的参数将会被
渲染到视图模板里
,用的相对是比较多的。
- 三.redirect
- 1.重定向,跳转新页面。参数为字符串,字符串中填写页面路径。一般用于 form 表单提交后,跳转到新页面。
每种方法都有相应的用武之地,这里我们先拿HttpResponse举例。
例如app01/views.py如下:
def index(request):
return HttpResponse('我是字符串,我后面的是一个html标签<input />')
urls.py如下:
from app01 import views
from django.urls import path
urlpatterns = [
path('index/', views.index),
]
此时访问127.0.0.1:8000/index/如下:
显然,可以传入一个或多个参数。
需要注意:
视图函数中的参数名必须与urls.py中尖括号中的参数名保持一致,否则会出错。
还可以传入多个参数,app01/views.py如下:
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('我是字符串,我后面的是一个html标签<input />')
def index_detail(request, index_id, index_name):
return HttpResponse('我的id是%s我的名字是%s' % (index_id, index_name))
urls.py如下:
from django.urls import path
from app01 import views
urlpatterns = [
path('index/',views.index),
# 这里是可以自行定义类型的哦,类型不同就会报错~
path('index/int:<index_id>/<index_name>', views.index_detail),
]
访问测试如下:
不仅如此,django还可以提供关键字传参,取参数值从request取值使用get()
方法获取,且关键字能写多个,即:127.0.0.1:8000/index/?id=1&name=sehun
,需要注意的是视图函数中的参数名必须与urls.py中尖括号中的参数名保持一致,否则会出错
。
views.py如下:
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('我是字符串,我后面的是一个html标签<input />')
def index_detail(request, index_id, index_name):
return HttpResponse('我的id是%s我的名字是%s' % (index_id, index_name))
def index_list(request):
index_id = request.GET.get('id')
index_name = request.GET.get('name')
return HttpResponse('id:%s 名字:%s' % (index_id, index_name))
urls.py中路由不需要进行处理,如下:
from django.urls import path
from app01 import views
urlpatterns = [
path('index/',views.index),
path('index/<index_id>/<index_name>', views.index_detail),
path('index_list/',views.index_list),
]
此时访问显示:
显然什么都不写也不会报错,只是会返回一个none(空)
的参数。
二. URL映射
1.URL路由分发
在项目中,一般不可能只有一个app,而是有多个app,如果把所有app的views中的视图都放在urls.py中进行映射,肯定会让代码显得冗长混乱,后期维护也不方便。并且还不能同时导入views会重名,必须使用 as 别名 来实现不重名
相当麻烦,因此Django提供了一个机制,即URL模块化,可以在app内部包含自己的url匹配规则,而在项目的urls.py中再统一包含这个app的urls,这需要用到include函数
。
- 首先我们先在app01下新建urls.py
app01/urls.py如下:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index),
path('index_detail/<int:index_id>/<index_name>', views.index_detail),
path('index_list/', views.index_list),
]
- 然后,我们通过
python manage.py startapp app02
命令再创建一个app名为app02。 - 在app02下新建urls.py
app02/urls.py如下:
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login),
]
主目录/urls.py如下:
from django.contrib import admin
from django.urls import path, include
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls')),
path('app02/', include('app02.urls')),
]
app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'root' and pwd == '123':
# 重定向到app01下的index
return redirect('/app01/index/')
else:
return render(request, 'login.html', {'msg': '用户名或密码错误'})
render和HttpResponse的区别是多了可传参数的渲染和html模板
,而redirect相当于重定向到目标路由位置
。
主目录/login.html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/app02/login/" method="post">
{% csrf_token %}
<p>用户
<input type="text" name="username"><span style="color: red;">{{ msg }}</span>
</p>
<p>密码
<input type="password" name="password">
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
模板语言标记:
- 使用{{}} 用于接收render传来的字典数据,在html模板中
先渲染在返回给浏览器
。 - 接收来的
数据名字要和后台视图传来的要一致
,不然拿不到。 - 如果在视图传来的数据是一个字典里嵌套这列表或字典,字典的可以通过
{{msg.keys}}
来获取value值,而列表的可以通过{{msg.0(列表下标从0开始)}}
来获取。
csrf_token:相当于一个登陆验证(留着以后再讲,明白是个登陆验证,不写是使用不了post请求的)
示意如下:
显然,可以正常访问。
可以看到,此时访问每个视图函数,必须加上在主目录/urls.py中path方法中传入的第一个参数,如访问app01下的app就必须在url中加入app01前缀
;
include方法的作用是拼接主目录/urls.py中path方法的第一个参数与每个app中urls.py中定义的路由,以形成完整的路由。
2.URL反向解析
随着功能的增加,路由层的 url 发生变化,就需要去更改对应的视图层和模板层的 url,非常麻烦,不便维护。
这时我们可以利用反向解析,当路由层 url 发生改变,在视图层和模板层动态反向解析出更改后的 url,免去修改的操作。
反向解析一般用在模板中的超链接及视图中的重定向。
拿上面的例子做比方
app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'root' and pwd == '123':
# 重定向到app01下的index
return redirect('/app01/index/')
else:
return render(request, 'login.html', {'msg': '用户名或密码错误'})
这里我们用了重定向的方法,访问到了app01下的index,但是如果在重定向较多,而代码的路由发生了更改,那么就要一个一个的更改,显然,这是让人头大的,稍有不慎,就会显入万劫不复…
所以django的路由模块中,还有一个方式。
方式是在url.py中的path方法中传入name参数即可,app01/urls.py如下:
from django.urls import path
from . import views
urlpatterns = [
path('index/', views.index,name='index'),
path('index_detail/<int:index_id>/<index_name>', views.index_detail),
path('index_list/', views.index_list),
]
此时app01下的index被我们取了一个index的别名。
但是要怎么样可以告诉redirect呢,此时name的好兄弟reverse
来了,它不仅可以给redirect渲染,还可以给html模板的post请求的
在模板 templates 中的 HTML 文件中,利用 {% url "路由别名" %} 反向解析。
<form action="{% url 'index' %}" method="post">
app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect, reverse
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'root' and pwd == '123':
return redirect(reverse('index'))
else:
return render(request, 'login.html', {'msg': '用户名或密码错误'})
效果还是一样的。
3.URL正则路径
有时候定义url匹配需要使用正则表达式来实现一些复杂的需求,这时候我们可以使用re_path
来实现,其参数和path一致,只不过第一个参数即route参数可以是一个正则表达式。
还有django.conf.urls.url
也支持正则表达式匹配路由,其底层也是调用的re_path(regex, view, kwargs, name)
方法。
显然,定义路由时path(’’, views.music)
,等价于re_path(r’^$’, views.music)
。
此时我们在主目录/urls.py中定义视图如下:
from django.contrib import admin
from django.urls import path, re_path, reverse, include
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls', namespace='app01')),
path('app02/', include('app02.urls', namespace='app02')),
# 首页
re_path(r'music.html$', views.music),
# 匹配默认参数
path('page', views.page),
# 匹配一般参数
path('page/<page_num>', views.page),
# 正则表达式
# re_path(r'^$', views.music),
re_path(r'retest/(?P<year>\d{4})$', views.year_re),
re_path(r'retest/(?P<month>\d{2})$', views.month_re),
]
其中,(?P\d{4})
是给正则表达式分组\d{4}
取名,即通过?P<>
给正则表达式分组取名,来与视图函数中的参数名匹配。
在app01/views如下:
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('我是字符串,我后面的是一个html标签<input />')
def index_detail(request, index_id, index_name):
return HttpResponse('我的id是%s我的名字是%s' % (index_id, index_name))
def index_list(request):
index_id = request.GET.get('id')
index_name = request.GET.get('name')
return HttpResponse('id:%s 名字:%s' % (index_id, index_name))
def login(request):
return render(request, 'login.html')
def music(request):
return HttpResponse('音乐网站首页')
def page(request, page_num=1):
return HttpResponse('音乐 第%s页' % page_num)
def year_re(request, year):
return HttpResponse('年份为:%s' % year)
def month_re(request, month):
return HttpResponse('月份为:%s' % month)
此时访问127.0.0.1:8000/music.html显示:
最后实现了自动匹配了。
4.URL命名空间
命名空间(英语:Namespace)是表示标识符的可见范围:
- 一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。
- 一个新的命名空间中可定义任何标识符,它们不会与任何重复的标识符发生冲突,因为重复的定义都处于其它命名空间中。
存在问题:路由别名 name 没有作用域,Django 在反向解析 URL 时,会在项目全局顺序搜索
,当查找到第一个路由别名 name 指定 URL 时,立即返回。当在不同的 app 目录下的urls 中定义相同的路由别名 name 时
,可能会导致 URL 反向解析错误。
解决:使用命名空间。
比如app01/urls.py和app02/urls.py修改如下:
app01/urls.py如下:
from django.urls import path
from . import views
urlpatterns = [
path('index/', views.index,name='index'),
path('index_detail/<int:index_id>/<index_name>', views.index_detail),
path('index_list/', views.index_list),
]
app02/urls.py如下:
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login),
path('index/', views.index,name='index'),
]
添加一个app02/urls.py下的index函数:
from django.shortcuts import render, HttpResponse, redirect, reverse
def index(request):
return HttpResponse('app02主页面')
此时访问127.0.0.1:8000/app02/login/显示:
很显然,reverse已经不知道是哪个index别名了,所以我们需要在用namespace='实例命名空间’
来实例命名空间。
主目录/urls.py如下:
from django.contrib import admin
from django.urls import path reverse, include,re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls', namespace='app01')),
path('app02/', include('app02.urls', namespace='app02')),
re_path(r'login.html$', views.music),
]
app01/urls如下:
from django.urls import path
from . import views
# 设定命名空间
app_name = 'app01'
urlpatterns = [
path('index/', views.index,name='index'),
path('index_detail/<int:index_id>/<index_name>', views.index_detail),
path('index_list/', views.index_list),
]
app02/urls如下:
from django.urls import path
from . import views
# 设定命名空间
app_name = 'app02'
urlpatterns = [
path('login/', views.login),
path('index/', views.index,name='index'),
]
app02/views如下:
from django.shortcuts import render, HttpResponse, redirect, reverse
def index(request):
return HttpResponse('app02主页面')
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'root' and pwd == '123':
return redirect(reverse('app01:index'))
else:
return render(request, 'login.html', {'msg': '用户名或密码错误'})
此时访问127.0.0.1:8000/app02/login/如下:
此时两者相同的命名就已经分开了,不再覆盖彼此。
假设访问注册首页重定向到文章页,app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect, reverse
def index(request):
return HttpResponse('app02主页面')
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'root' and pwd == '123':
return redirect(reverse('app01:index_detail'))
else:
return render(request, 'login.html', {'msg': '用户名或密码错误'})
app01/urls如下:
from django.urls import path
from . import views
app_name = 'app01'
urlpatterns = [
path('index/', views.index, name='index'),
path('index_detail/<int:index_id>/<index_name>', views.index_detail, name='index_detail'),
path('index_list/', views.index_list),
]
此时访问127.0.0.1:8000/app02/login/并登陆成功会报错。
NoReverseMatch at /app02/login/
Reverse for 'index_detail' with no arguments not found. 1 pattern(s) tried: ['app01/index_detail/(?P<index_id>[0-9]+)/(?P<index_name>[^/]+)$']
很明显,reverse()
方法中没有传入index_id,index_name参数,路径参数是通过kwargs
以字典形式来传递的。
现传入参数app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect, reverse
def index(request):
return HttpResponse('app02主页面')
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'root' and pwd == '123':
return redirect(reverse('app01:index_detail', kwargs={'index_id': 2, 'index_name': 'sehun'}))
else:
return render(request, 'login.html', {'msg': '用户名或密码错误'})
此时访问127.0.0.1:8000/app02/login/如下:
显然,也获取到了参数。