Django视图和URL配置
一、MTV模式
1、概述
Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:
- M 代表模型(Model):负责业务对象和数据库的关系映射(ORM)。
- T 代表模板 (Template):负责如何把页面展示给用户(html)。
- V 代表视图(View):负责业务逻辑,并在适当时候调用Model和Template。
Django中提供了View组件用来充当MVC中的C-controller。
2、view的主要职责
- 接收请求
- 调度Model,处理业务逻辑
- 响应请求
二、基本开发流程
1、创建App,定义view中的函数
在每个App的views.py中,定义一个hello函数
1 | def hello(request): # 必须要有一个request参数 |
2、函数返回响应
每个view必须要有响应,即返回一个HttpResponse
1 | from django.shortcuts import HttpResponse |
3、定义访问路径
在urls.py中,为每个view定义访问路径
1 | from django.urls import path |
4、启动服务
django内置了一个供开发使用的 web-服务器,虽然不适合生产环境,但用户开发测试,特别便利。
1 | python manage.py runserver #默认ip和端口 localhost:8000 和使用 run启动是一样 |
注意:修改了ip和port后,需要到settings.py中增加一个设置:ALLOWED_HOSTS = [“*”]
1 | 127.0.0.1:8000/hello/ 在浏览器中输入的URL |
5、访问view
1 | http://localhost:8000/hello/ |
1 | 总结:启动服务器的几种方式 |
三、URL配置
URL配置的本质是 URL 模式以及要为该 URL 模式调用的视图函数之间的映射表。
该配置用于告诉 Django,对于这个 URL 调用这段代码,对于那个 URL 调用那段代码。
例如,当用户访问xxx/hello时,调用视图函数hello(),这个视图函数存在于Python模块文件views.py中。
1、一般配置
1 | from django.contrib import admin |
2、正则配置
1 | from django.contrib import admin |
注意: 在使用正则时,^
和 $
可以保证精准匹配。 ^表示以什么开头, $ 表示以什么结尾。
3、URL技巧(path管理)
一个Project会有很多App,每个App会有很多View函数,如果都在一个urls.py中配置,则项目的管理和维护成本将急剧上升。
每个App不仅逻辑独立,也希望在url-pattern的配置上也相互隔离。
因此我们希望每个App都应该有一个独立的url-conf,在每个app下创建一个新的urls.py文件,然后在全局的urls.py中来包含各个app的url-conf。
- 在App目录下,新建一个urls.py,并配置自己的url路径:
1 | from django.contrib import admin |
- 在全局urls.py中,使用include包含各个app的url-conf:
1 | from django.contrib import admin |
补充:
如果在一个app的众多path中,又有关系逻辑关联紧密的path,可以进一步管理:
1 | from django.contrib import admin |
至此:View的url配置则有了管理方案:
- 每个App定义自己的urls.py,其中定义自己的url-conf。
- Project的全局urls.py中引入各个App的url-conf,并定义访问前缀,区分、隔离各个App
- App内部的url-conf可以根据逻辑进一步区分、隔离。
四、命名路径
1、基本使用
语法:
1 | path("xxx/<参数名1>/<参数名2>/../", xxx) # <参数名1> 此种路径可以匹配任何内容 |
示例:
1 | path("hello/<name>/<age>/",views.hello) # localhost:8000/hello/mr_lee/18/ |
注意:有了命名路径,view的函数中必须有对应的接收参数。
2、命名路径转换器
非正则命名路径中可以使用转换器,以约束每个路径捕获到的数据的类型
- int:接收整数
- str:字符串(了解)
- path:可以接收包含”/“的路径(了解)
如果路径上的值不符合转换器类型,则访问不到对应的view函数(404页面)
1 | path("hello/<int: age>/<str: name>/",hello) |
3、正则命名路径
1、正则路径匹配且捕获数据
使用正则的“捕获组”的格式(regex)也可捕获到url路径中的数据。
即用括号包围的正则,就是一个捕获组,不仅匹配路径,还可以捕获到数据。
1 | re_path("hello/(\d{3})/",hello1) # http://localhost:8000/hello/125/ |
2、正则命名路径且捕获数据
1 | re_path("hello/(?P<age>\d{3})/",hello1) # http://localhost:8000/hello/125/ |
1 | 总结: |
五、请求处理(重中之重)
(1)用户发送请求 (2)服务器接收请求 (3)服务器处理请求并返回响应
1、发送请求的形式
在Web中发送页面请求的形式一般有三种:
<a href="xxx">
超链接location.href="xxx"
<form action="xxx">
表单
1 | <html> |
2、接收请求参数
客户端(浏览器)访问服务器时,需要向服务器传递数据(请求参数)。
方式一:接收路径中的参数
- 命名路径
- 正则路径
- 正则命名路径
方式二:常规的参数接收,通过request对象接收
3、超链接传参
超链接传参格式: url/?key=value&key2=value2
1 | <a href="http://localhost:8000/hello5/?id=115&name=mr_lee">超链接传参</a> # html超链接 |
附:location.href传参与超链接传参类似。
4、表单传参
表单中的控件,name属性值为参数名,value属性值为参数值。
1 | <for、m action="http://localhost:8000/hello6/" method="get"> |
5、request接收请求参数
5.1 接收get请求参数
- request.GET[‘参数名’]
- request.GET.get(“参数名”)
1 | path('hello6/', views.hello6) |
5.2 接收post请求参数
- request.POST[‘参数名’]
- request.POST.get(“参数名”)
1 | <form action="http://localhost:8000/hello6/" method="post"> |
1 | path('hello6/', views.hello6) |
注意:
- GET[‘age’] 如果没有键则报错
- GET.get(‘age’) 如果没有键返回None
6、CSRF
CSRF(Cross-site request forgery)跨站请求伪造,是一种常见的网络攻击手段。
7、CSRF基本使用
Django为我们提供了防范CSRF攻击的机制。
默认情况下,使用django-admin startproject xxx
命令创建工程时,CSRF防御机制就已经开启了。如果没有开启,请在MIDDLEWARE设置中添加’django.middleware.csrf.CsrfViewMiddleware’。
对于GET请求,一般来说没有这个问题,CSRF通常是针对POST方法的!
在含有POST表单的模板中,需要在其<form>
表单元素内部添加csrf_token
标签,如下所示:
1 | <form action="" method="post"> |
8、CSRF实现原理
- 在发送请求时,通过CsrfViewMiddleware 在服务器端生成一个随机的token(令牌),在render渲染模板时存放于form单的隐藏域和浏览器中
- 当再次form发送请求时,会携带隐藏域令牌和cookie中的令牌,此时CsrfViewMiddleware 会先于View执行,去判断两块令牌是否一致,验明正身。
9、Get和Post请求区别
- POST安全性好于GET,GET中的请求参数直接暴露于url上;传递密码等敏感数据时,不使用GET。
- GET对数据长度有限制,URL 的最大长度是 2048 个字符,POST无限制。
- HTTP的语义中GET请求用来查询数据,POST用来提交数据
- GET只允许 ASCII 字符。POST可以传递各种数据,二进制也可以。
- GET可以缓存,POST不能缓存
实用技巧:请求中有隐私数据(如密码) 或 有较多数据时不建议使用Get
实用场景:Get常用来做查询和删除请求,Post常用来做增加和更新请求
10、接收多值参数
- 当请求参数中同一个key上有多个值,View中如何接收?
http://localhost:8000/test1/user/vip/a/?age=18&age=19&name=Mr_lee
1 | <form action="http://localhost:8000/hello7" method="GET"> |
hobby有多个值,GET[‘hobby’]得到的值是什么 ?
http://localhost:8000/hello7/?hobby=football&hobby=basketball&hobby=volleyball
1 | def hello7(request): |
- 使用GET.getlist(“xxx”)或POST.getlist(“xxx”)来接收多个相同key的值:
1 | def hello7(request): |