一、获取表单提交的数据

在 [Python自学] day-18 (2) (MTV架构、Django框架)中,我们使用过以下方式来获取表单数据:

user = request.POST.get('username', None)

这种获取方式可以获取来自表单的单个数据,例如<input type='text'/>的数据。

除了以上这种最简单的数据获取方式,我们还需要获取例如<input type='checkbox' />、<input type='file' />、<select>等标签的数据:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MyPage</title>
<style>
p{
border: 1px solid #dddddd;
display: inline-block;
}
</style>
</head>
<body>
<form action="/mypage" method="post" enctype="multipart/form-data">
<!-- radio单选 -->
<p>性别:</p>
<div>
男:<input type="radio" name="gender" value="1"/>
女:<input type="radio" name="gender" value="2"/>
</div>
<!-- checkbox多选 -->
<p>喜好:</p>
<div>
足球:<input type="checkbox" name="favor" value="11"/>
篮球:<input type="checkbox" name="favor" value="22"/>
游泳:<input type="checkbox" name="favor" value="33"/>
</div>
<!-- 单选select -->
<p>来自哪个城市:</p>
<div>
<select name="city">
<option value="cd">成都</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
</div>
<!-- 多选select -->
<p>喜欢哪些城市:</p>
<div>
<select name="favorcity" multiple>
<option value="cd">成都</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
</div>
<!-- 上传文件 -->
<div>
<input type="file" name="filetrans"/>
</div>
<div style="height: 48px;line-height: 48px;">
<input type="submit" value="提交"/>
</div>
</form>
</body>

在views.py中,我们可以通过以下方式来获取对应的数据:

def mypage(request):
if request.method == 'POST':
print(request.POST.get('gender', None)) # 获取radio单选数据,打印单个数据,例如'2'表示"女"
print(request.POST.getlist('favor', None)) # 获取checkbox的多选数据,打印value组成的列表 ['11','22']
print(request.POST.get('city', None)) # 获取select的单选数据,打印单个数据,例如'cd'表示"成都"
print(request.POST.getlist('favorcity', None)) # 获取multiple select标签的多选数据,打印列表['cd','sh'] # 获取文件对象
recv_file = request.FILES.get('filetrans', None)
print(recv_file.name)
# 从obj.chunks()中循环获取文件的块,并写入同名文件
with open(os.path.join('upload', recv_file.name), 'wb') as f:
for i in recv_file.chunks():
f.write(i) return render(request, 'mypage.html')

特别注意:在上传文件的时候,<form>表单必须要有 enctype="multipart/form-data" 属性,否则会将文件当做字符串提交(也就是说后台只能收到文件的名称)。

二、FBV和CBV

FBV:Function base view,基于函数的视图。

CBV:Class base view,基于类的视图。

1.FBV

在之前的章节中,我们在APP的views.py中写了很多请求处理函数(视图函数),使用函数来处理请求,就叫做FBV。

2.CBV

如果我们使用一个类来处理一个URL,则称为CBV,例如在APP的views.py中定义一个处理类:

from django.views import View

# 处理类必须继承自View类
class MyPage(View):
# get方法专门处理GET请求
def get(self, request):
print(request.method)
return render(request, 'mypage.html') # post方法专门处理POST请求
def post(self, request):
print(request.method)
return render(request, 'mypage.html')

我们查看View类的源码,可以看到:

class View:
"""
Intentionally simple parent class for all views. Only implements
dispatch-by-method and simple sanity checking.
""" http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] ......
......

我们可以定义 http_method_names 列表中所列出的所有请求类型对应的方法。

3.CBV中的执行过程

我们查看View父类的源码:

    def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)

我们可以看到使用反射的时候,参数中将请求方式转化为小写。所以,我们实现的方法名必须是小写的。例如get()、post()、put()。如果请求的方法不在允许的列表中,则返回405错误(参考源码中的 self.http_method_not_allowed方法)。

4.重写父类中的dispatch方法,实现自定义功能

# 处理类必须继承自View类
class MyPage(View): # 重写父类方法
def dispatch(self, request, *args, **kwargs):
print('before')
result = super(MyPage, self).dispatch(request, *args, **kwargs)
print('after')
return result # get方法专门处理GET请求
def get(self, request):
print(request.method)
return render(request, 'mypage.html') # post方法专门处理POST请求
def post(self, request):
obj = request.FILES.get('filetrans')
if obj is not None:
with open(os.path.join('upload',obj.name),'wb') as f:
for item in obj.chunks():
f.write(item) return render(request, 'mypage.html')

在调用父类dispatch方法前后可以实现自定义功能。

总结:FBV和CBV用哪个更好?

FBV和CBV没有哪个好,哪个不好。在生产中都可以使用。

三、实现详情页面(动态url)

在后台管理页面中,我们经常看到一个列表(例如用户列表),点击其中一条,可以跳转到详情页面。

1.用户列表页面html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UserList</title>
</head>
<body>
<ul>
{% for key,value in user_dict.items %}
<li><a href="/details/?nid={{ key }}"> {{ value.name }} </a></li>
{% endfor %}
</ul>
</body>
</html>

2.添加urls.py映射关系

from cmdb import views

urlpatterns = [
path('admin/', admin.site.urls),
path('login', views.login),
path('home', views.home), # 后台管理页面映射,映射到cmdb.views.home方法
path('mypage', views.MyPage.as_view()),
path('users', views.user_page),
]

3.用户列表视图函数

USER_DICT = {
'': {'name': 'Alex', 'email': 'Alex@163.com'},
'': {'name': 'Jone', 'email': 'Jone@163.com'},
'': {'name': 'Leo', 'email': 'Leo@163.com'},
'': {'name': 'Eric', 'email': 'Eric@163.com'}
} # /users页面的视图函数
def user_page(request):
return render(request, 'users.html', {'user_dict': USER_DICT})

4.用户列表页面实现效果:

5.用户详情页面html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Details</title>
</head>
<body>
<h1>详细信息</h1>
<h6>用户名: {{ detail_info.name }}</h6>
<h6>邮箱: {{ detail_info.email }}</h6>
</body>
</html>

6.添加urls.py中的映射关系

from cmdb import views

urlpatterns = [
path('admin/', admin.site.urls),
path('login', views.login),
path('home', views.home), # 后台管理页面映射,映射到cmdb.views.home方法
path('mypage', views.MyPage.as_view()),
path('users', views.user_page),
path('details/', views.details),
]

7.用户详情视图函数

# /details页面的视图函数
def details(request):
detail_info = {}
# 如果从GET数据中获取到nid,则取相应用户的详情
if request.method == 'GET':
nid = request.GET.get('nid')
detail_info = USER_DICT[nid]
return render(request, 'details.html', {'detail_info': detail_info})

8.详情页面实现效果

四、伪静态URL方式实现详情页面

在第三节中,我们使用了"detail/?nid=3"这种形式的参数传递方式,可以从GET中获取相应的数据。但这种形式的URL为动态URL,在SEO中权重很低。

所以目前比较流行的做法是,使用"detail-3.html"这种方式才传递参数"3"。这种方式被SEO看做是静态URL,具有比较高的排名权重。

1.修改users.html文件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UserList</title>
</head>
<body>
<ul>
{% for key,value in user_dict.items %}
<li><a href="/details-{{ key }}.html"> {{ value.name }} </a></li>
{% endfor %}
</ul>
</body>
</html>

2.修改urls.py映射

from django.contrib import admin
from django.urls import path
from django.urls import re_path from cmdb import views urlpatterns = [
path('admin/', admin.site.urls),
path('login', views.login),
path('home', views.home), # 后台管理页面映射,映射到cmdb.views.home方法
path('mypage', views.MyPage.as_view()),
path('users', views.user_page),
re_path('details-(\d+).html', views.details),
]

这里要使用正则表达式(导入re_path模块)来进行映射匹配,Django会自动将"()"中匹配到的字符串作为参数传递给views.details()

3.修改视图函数

# /details页面的视图函数
def details(request, nid):
# 取相应用户的详情
detail_info = USER_DICT[nid]
return render(request, 'details.html', {'detail_info': detail_info})

4.伪静态URL详情页面实现效果

五、基于正则的URL

前面第四节我们已经使用了基于正则的URL映射。如下代码所示:

from django.contrib import admin
from django.urls import path
from django.urls import re_path from cmdb import views urlpatterns = [
path('admin/', admin.site.urls),
path('login', views.login),
path('home', views.home), # 后台管理页面映射,映射到cmdb.views.home方法
path('mypage', views.MyPage.as_view()),
path('users', views.user_page),
re_path('details-(\d+).html', views.details),
]

要使用正则,必须使用re_path模块。

如果正则中有两个分组

re_path('details-(\d+)-(\d+).html', views.details)

那么对应视图函数就应该是:

def details(request, param1, param2):
pass

正则表达式中匹配到的数据会按顺序传递给details()函数。

我们可以使用以下方式,让其指定传递的参数名:(推荐使用)

re_path('details-(?P<nid>\d+)-(?P<uid>\d+).html', views.details)

"<>"中的参数名就表示正则表达式匹配到的数据指定传递给哪个参数,所以我们的视图函数参数顺序可以任意:

def details(request, nid, uid):
pass def details(request, uid, nid):
pass

参数的顺序变了,但值都能传递正确。

当我们不确定参数个数,或者为了方便,可以将视图函数写成:

def details(request, *args, **kwargs):
pass

当我们使用前面那种按顺序传递参数的方式,参数就会被传递到"*args"中(元组)。

当后者按名称传递的方式,参数就会被传递到"**kwargs"中(字典)。

六、修改URL的便捷方式({% url 'url_name' %})

当我们对urls.py中的某个映射进行修改时,使用这个URL的地方也要修改:

例如修改urls.py中的其中一条映射:

urlpatterns = [
path('admin/', admin.site.urls),
path('login', views.login),
path('home', views.home),
# path('mypage', views.MyPage.as_view()),
path('mypage12376sjhdfjnwjer', views.MyPage.as_view()),
path('users', views.user_page),
re_path('details-(\d+).html', views.details),
]

我们此时需要修改html中表单提交的目的地址:

<form action="/mypage12376sjhdfjnwjer" method="post" enctype="multipart/form-data">

Django为我们提供了一种便捷的方式(其他框架可能没有):

我们在定义urls.py中的映射关系时,不管匹配字符串是什么我们都可以为其定义一个"name":

urlpatterns = [
path('admin/', admin.site.urls),
path('login', views.login),
path('home', views.home),
# path('mypage', views.MyPage.as_view()),
path('mypage12376sjhdfjnwjer', views.MyPage.as_view(), name='mypage'),
path('users', views.user_page),
re_path('details-(\d+).html', views.details),
]

在html中,我们就不需要写匹配字符串了,而是如下:

<form action="{% url 'mypage' %}" method="post" enctype="multipart/form-data">

此时,访问对应的页面,可以正常提交:

Django提供name的目的:

让我们可以根据name来构建我们需要的URL,例如在上面的例子中,name="mypage"代表着URL "/mypage12376sjhdfjnwjer"。。

当我们的表单提交目的URL为这个URL时,我们可以直接使用{% url 'mypage' %}。

思考一个场景,当我们处于一个分页页面,例如第10页,该页面对应的URL映射为:

re_path('mypage12376sjhdfjnwjer/(\d+)', views.MyPage.as_view(), name='mypage')

我们处在的页面URL为:http://127.0.0.1/mypage12376sjhdfjnwjer/10/

此时,假设该页面右上角有登录按钮,我们点击进行登录。页面会进行跳转,我们可以把{% url 'mypage' 10 %}串到登录按钮的href中。例如"/login/mypage12376sjhdfjnwjer/10/".

这样,/login页面可以拿到我们点击登录时正处于的页面,在登录完成后,页面还可以跳转回之前浏览的页面。

从上面可以看出,当我们的URL是正则匹配时,我们可以使用传参的方式生成想要的URL:

re_path('mypage12376sjhdfjnwjer/(\d+)/', views.MyPage.as_view(), name='mypage')  # 对应{% url 'mypage' 10 %}
re_path('mypage12376sjhdfjnwjer/(\d+)/(\d+)/', views.MyPage.as_view(), name='mypage')  # 对应{% url 'mypage' 10 13 %}
re_path('mypage12376sjhdfjnwjer/(?P<nid>\d+)/', views.MyPage.as_view(), name='mypage')  # 对应{% url 'mypage' nid=10 %}
re_path('mypage12376sjhdfjnwjer/(?P<nid>\d+)/(?P<uid>\d+)/', views.MyPage.as_view(), name='mypage')  # 对应{% url 'mypage' nid=10 uid=13 %}

当然,我们也可以在登录页面的视图函数中直接生成:

# 登录操作,登录完毕后跳转到某页
def login(request, *args, **kwargs):
# 登录操作
# todo.. # 直接字符串拼接
url = '/mypage12376sjhdfjnwjer/10/13/'
# 使用django提供的函数实现拼接
from django.urls import reverse
url = reverse('mypage', args=(10,)) # 对应正则:mypage12376sjhdfjnwjer/(\d+)/
url = reverse('mypage', args=(10, 13,)) # 对应正则:mypage12376sjhdfjnwjer/(\d+)/(\d+)/
url = reverse('mypage', kwargs={'nid': 10}) # 对应正则:mypage12376sjhdfjnwjer/(?P<nid>\d+)/
url = reverse('mypage', kwargs={'nid': 10, 'uid': 13}) # 对应正则:mypage12376sjhdfjnwjer/(?P<nid>\d+)/(?P<uid>\d+)/
# 登录完毕后跳转回某页
return redirect(url)

七、路由分发

目前,我们只有一个urls.py,位于Django工程目录。我们所有的APP的映射都写在一起,显得比较杂乱,也不便于多人协作。

我们可以使用Django提供的路由分发功能。

首先,我们在最上层urls.py(工程目录中的urls.py)中,进行修改:

from django.contrib import admin
from django.urls import path
from django.urls import include

urlpatterns = [
path('cmdb/', include("cmdb.urls")),
path('mgmt/', include("mgmt.urls")),
]

这个最上层urls.py会将所有http://127.0.0.1:8000/cmdb/xxx的URL分发给APP cmdb的urls.py进行处理。

将所有http://127.0.0.0:8000/mgmt/xxx的URL分发给APP mgmt的urls.py处理。

我们分别在两个APP文件夹中创建urls.py:

分别在cmdb和mgmt的urls.py写各自的路由映射,例如在cmdb的urls.py中有如下映射:

from django.contrib import admin
from django.urls import path
from django.urls import re_path from cmdb import views urlpatterns = [
path('admin/', admin.site.urls),
path('login', views.login),
path('home', views.home),
# path('mypage', views.MyPage.as_view()),
path('mypage12376sjhdfjnwjer', views.MyPage.as_view(), name='mypage'),
path('users', views.user_page),
re_path('details-(\d+).html', views.details),
]

那么访问 "mypage12376sjhdfjnwjer"这个页面的话,要使用 http://127.0.0.1:8000/cmdb/mypage12376sjhdfjnwjer:

这样就完成了按APP进行路由分发的功能。

[Python自学] day-19 (1) (FBV和CBV、路由系统)的更多相关文章

  1. Python学习(三十一)—— Django之路由系统

    转载自:http://www.cnblogs.com/liwenzhou/p/8271147.html Django的路由系统 Django 1.11版本 URLConf官方文档 URL配置(URLc ...

  2. python第一百零五天 ---Django 基础 路由系统 URL 模板语言 ORM 操作

    一 路由系统 URL 1 url(r'^index/',views.index) url(r'^home/', views.Home.as_view()) 2 url(r'^detail-(\d+). ...

  3. [Python自学] day-21 (2) (Cookie、FBV|CBV装饰器)

    一.什么是Cookie 1.什么是Cookie? Cookie是保存在客户端浏览器中的文件,其中记录了服务器让浏览器记录的一些键值对(类似字典). 当Cookie中存在数据时,浏览器在访问网站时会读取 ...

  4. python 视图 (FBV、CBV ) 、Request 和Response对象 、路由系统

    一.FBV和CBV1.基于函数的view,就叫FBV(Function Based View) 示例: def add_book(request): pub_obj=models.Publisher. ...

  5. python 全栈开发,Day84(django请求生命周期,FBV和CBV,ORM拾遗,Git)

    一.django 请求生命周期 流程图: 1. 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post, ...

  6. Python菜鸟之路:Django 路由补充1:FBV和CBV - 补充2:url默认参数

    一.FBV和CBV 在Python菜鸟之路:Django 路由.模板.Model(ORM)一节中,已经介绍了几种路由的写法及对应关系,那种写法可以称之为FBV: function base view ...

  7. day54_9_18视图层某内部原理(fbv和cbv)与模板层

    一.render内部原理. 在render中往往需要返回三个参数,request,模板和一些键值对. 键值对中存储的是需要对模板渲染的值. 如果手动实现可以如下: from django.templa ...

  8. django请求生命周期,FBV和CBV,ORM拾遗,Git

    一.django 请求生命周期 流程图: 1. 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post, ...

  9. django——FBV与CBV

    引言 FBV FBV(function base views) 就是在视图里使用函数处理请求. 在之前django的学习中,我们一直使用的是这种方式,所以不再赘述. CBV CBV(class bas ...

随机推荐

  1. 数据结构 -- 队列Queue

    一.队列简介 定义 队列(queue)在计算机科学中,是一种先进先出的线性表. 它只允许在表的前端进行删除操作,而在表的后端进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有 ...

  2. 1.3Security:权限管理,过滤、监听、拦截

    Security:权限管理 常用权限拦截器 SecurityContextPersistenceFilter 以前是HttpSesstionContextIntegrationFilter,位于过滤器 ...

  3. centos7 宝塔php7安装mongodb扩展

    一.下载.解压源码 下载地址:https://pecl.php.net/package/mongodb wget -c https://pecl.php.net/get/mongodb-1.5.3.t ...

  4. 适合新手的160个creakme(三)

    先跑一下,这个程序应该是有定时器,多久之后自动开启,测试一下输入,序列号以字母方式输入会出现类型不匹配,之后程序自动退出 但是如果以数字方式输入序列号,则会出现,Try Again,所以这里序列号应该 ...

  5. T100——程序从标准签出客制后注意r.c和r.l

    标准签出客制后,建议到对应4gl目录,客制目录 r.c afap280_01 r.l afap280_01 ALL 常用Shell操作命令: r.c:编译程序,需在4gl路径之下执行,产生的42m会自 ...

  6. volatile 关键字(修饰变量)

    目录 volatile 关键字(修饰变量) 1. 含义 2. 作用 3. 如何保证可见性 4. 如何禁止指令重排序优化 5. volatile 是不安全的 6. volatile 不适用场景 vola ...

  7. System performance tools

    System performance tools ============ End

  8. LeetCode:184.部门工资最高的员工

    题目链接:https://leetcode-cn.com/problems/department-highest-salary/ 题目 Employee 表包含所有员工信息,每个员工有其对应的 Id, ...

  9. JavaScript中的setTimeout、setInterval和随机函数制作简易抽奖小程序

    几乎所有计算机语言有都内置随机函数.当然这种随机,人们习惯称为伪随机数发生器,产生的是一个[0,1)之间的一个小数.再通过简单算术运算生成一个符合需求的整数.JS中通用公式通常为parseInt(Ma ...

  10. Java秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化)

    转自:https://blog.csdn.net/qq_41305266/article/details/81146716 一.思路:减少数据库访问 1.系统初始化,把商品库存数量加载到Redis 2 ...