巨蟒django之CRM3 添加和编辑客户&&公户和私户的展示和转换
昨日内容回顾:
day66 1. 内容回顾 1. 数据的展示 数据通过ORM查询出来 对象列表 QuerySet 1. 普通的字段 对象.字段名 ——》 数据库中的值 2. choices ((1,'男')) 对象.字段名 ——》 数据库中的值 1 对象.get_字段名_display() ——》 数据库中的值对应的显示值 男 3. 外键 对象.外键 ——》 关联的对象 定义 __str__ __repr__ 对象.外键.字段名 4. 自定义方法 多对多: def show_class(self): return ' | '.join([str(i) for i in self.class_list.all()]) HTML代码: def show_status(self): status_color = { 'signed': 'green', 'unregistered': '#208c72', 'studying': 'yellow', 'paid_in_full': 'blue', } return mark_safe('<span style="background-color: {};color: white;padding: 2px">{}</span>'.format( status_color.get(self.status), self.get_status_display())) 2. 分页
__str__方法和__repr__方法
打印的是两个对象
查看源码:
上图代表的是我们自己定义上str方法,得到的是名字,而不再是对象
结论:首先调用__str__方法,再调用__repr__方法.
上边,打印的是一个列表,列表本身也是一个类
上图是列表类
分页的思路(如何做的?):
首先有个当前的页码数,算取页面的起始值和终止值,根据每页的个数切取值,如果每页是10条,那么第一页就是1-10,第二页就是11-20,这样就能实现功能了,只需要知道当前的页码数和每页要显示的数据条数,就可以实现功能了,为了好看,用户用的方便,让用户可以点击,最开始展示所有页码数,总的数据量除以每页显示的条数,这样一算就知道要显示多少页码,多少个a标签,在这里要考虑一个问题?能否除尽的问题,用到了divmod,得到两个结果一个是整除的结果,另一个是余数.有余数的话,总页码要加上1,有了总的页码数,我们可以循环出所有的页码.此时的问题是,页码数太多了,我们需要定义最多能够显示多少条.定义一个max_show,只需要定住start_page和end_page,页码的起始值和终止值,首先要分情况套路你这个问题,现在有的页码数不够最大显示页码数,我们只需要设置显示现在有的页码数就可以了,如果多的话,按照最大显示页码数显示就可以了,这时候还需要考虑其他一些问题,当前页码数在中间,左边显示也就是当前页码数减去一半显示页码数,右边显示的是当前页码数加上显示页码数的一半.我们只需要循环这两个值就可以得到结果了,但是循环过程中存在问题了,左边极值的问题,只需要判断,左侧显示的数,到达左侧不能显示0和负数,需要设置page_start,起始值设置为1,page_end等于最大显示页码数就可以了,右边极值是不能超过总的页码数,通过运算,当前页码数加上显示页码数的一半大于等于总页码数,就不能向外边写了,也就是page_end等于总页码数,
总页码数减去最大显示码码数+1,也就是四种情况,(第一种是,总页码数小于最大显示页码数,),(第二种是,处理左边的极值的情况),(第三种是处理右边的极值情况),最后一种是正常的情况
要记忆这个实现功能的步骤,最后实现封装搭配类中,
最后需要封装类的三个方法,切片起始值和终止值还有一个l生成的字符串,
注重这个实现过程,是怎样的!!!
1.添加客户
添加顾客的功能,在app中也就是crm中的urls添加,
目前存在的问题,所有的视图函数都先crm也就是app中的views.py写,会导致这个文件也来越大
下面我们进行单独分开,做代码修改时候好改一些
注意:视图函数写在哪里都可以的,只需要在下图中找到对应关系就可以了
在项目中的app,也就是crm下新建views文件夹,在创建一个customer.py文件,将客户(顾客)相关的信息放在里边
上边显示的重名了,不知道结果,路径会有一些问题,需要拆分一些再说
将登陆,注册和首页以及分页放到auth.py文件中
注释下图中的内容
注释上边红框内的两行
我们直接删除crm下的view.py因为刚才,我们已经移动过了,现在这个view.py文件夹是空的,
修改之后的crm路径结果:
区分之后,让业务相关更加清晰
注意,这个models是否加s的写法
复制下面这条语句
在app,也就是crm下templates创建一个下图所示的customer_add.html页面
看一下models.py中的Cusomer类中有哪些字段修改之后的结果,见下图
我们先展示上边qq这一个字段.
运行项目:
报错
上图中的fields没有加上s,上图已经加上了,错误可以排除了
没出现框的原因是:
OR写反了.
通过父类继承,得到结果,下面是写的父类(BootstrapForm样式)
下边的方法直接继承,基类
此时,我们再运行,得到如下结果
下面,我们开始显示字段
但是一个一个写字段太麻烦了,应该怎样写?
打印下面的结果form_obj
运行:
服务端打印下面的内容:
运行:
这时候,我们就不需要一条一条写了
报错:修改成下面的样子,在具体的for循环内部,不能再有form_obj
运行,得到结果,但是图标有点飘.
下面我们进行处理一下.
咨询课程出错的原因是,给咨询课程也加上了form-control的效果
通过找到的对应字段,进行处理
上边是先加上,然后我们在去掉这个control-form
这个时候得到的结果就正常了
下面我们开始判断,提交请求,在函数中
直接提交空的,得到的结果是:
这样信息,就直接添加到下面的页面中了
错误的显示还是不够明显,我们需要添加上bootstrap样式的效果,
找到上边对应的校验状态,
has-error//has-success等等
,在添加页面加上这个属性
运行,颜色发生了变化
加上错误提示:
修改得到结果
这个时候,我们需要的是,有错误才会进行显示
需要进行判断一下,有错误显示,没错误就不显示了
下面我们再整合到一行
运行,
这个时候,成功完成了颜色的变化和提示,注意,看出这个效果要点击"保存",才能够看出来
补充:
再看一下添加客户的功能:
最后找这个BaseModelForm
这样就找到了这三个方法:
循环需要调用,self.fields也就是这个有序字典,key指的是当前字段的名字,对应的值是当前字段的对象,拿到当前字段的名字
取值,调用的是哪个方法?也就是下面的这个方法__getitem__方法:
上图做的是,最后返回的是每一个字段的对象做的一个变化,所以循环出的结果,也就是input框中的内容
也就是在模板中直接寻找就可以了
也就是调用了iter方法和getitem这两个方法.也就是循环fields等于的__all__值
也就是说,我们拿到的当前字段的对象
上边的页面太简单,我们需要添加一个标题,让我们知道是"添加页面"还是"编辑页面",需要添加一个面板的东西
通过F12拿对应的面板代码:
结果:
运行:
运行:(向上移动了70px)
将模板也中的下面内容注释掉,去掉"首页"和"客户管理"
运行:
将上边的面板修改成绿色的
运行:
2.编辑客户和整合
这个客户里边没有主键,我们在这里就要写一个
赋值一份customer_add.html到customer_edit.html
注意,上图的注释也是很重要的
pk在这里会默认拿到主键,如果硬ID的可能还需要改.
下面,我们需要通过图标库,来找响应的图标
Ctrl+F搜索edit,编辑图标
操作完成后,再将i标签,放在a标签里边
运行,得到结果:
这样就得到了编辑的图标库里边的样式了
点击这个"编辑"图标,进入下图所示的界面:
我们先去掉出生日期这一行:
这样就得到这样的页面
因为不做删除功能,我们就不要写,删除功能是很忌讳的东西
再找一个加号
运行,得到的结果如上图
再加上一个样式:
运行:
可以将图标变小一点:
让标签产生一点距离:
现在,点击添加按钮和编辑按钮,就可以跳转到响应的页面,但是需要修改,跳转到响应的页面可以进行修改和添加,怎么处理?也就是处理POST请求
如果qq什么都不填写,会报错
得到如上结果:
也就是上边的图就有了错误提示
点击保存:得到结果
上边的这个添加和编辑的代码不超过三行是不同的,下面我们进行合并一下
未添加改变之前的代码:
customer_add.py
#添加客户 def customer_add(request): #不包含数据的form form_obj=CustomerForm() if request.method=='POST': # 包含用户提交数据的form form_obj=CustomerForm(request.POST) #注意和这个form_obj在这里已经重新定义了,已经包含了提交数据 if form_obj.is_valid(): #我们进行验证 #对数据进行校验 form_obj.save() #保存,创建一个对象 #跳转到展示页面 return redirect(reverse('customer_list')) return render(request,'customer_add.html',{'form_obj':form_obj}) # 编辑客户 def customer_edit(request,edit_id): obj=models.Customer.objects.filter(pk=edit_id).first() #注意,这里是主键代表pk首字母,知道customer这张表 #pk代表主键,edit_id代表前端传递过来的ID #models.Customer.objects.filter(pk=edit_id)代表对象列表,加上first(),代表取第一个 # form_obj = CustomerForm(),一开始,这个代表的是空的,不包含原始数据的 #如果需要里边的原始数据,需要加上instance=obj,就可以得到数据了 #instance代表实例的意思 #上边我们将对象obj传递给instance,这样我们就得到了包含原始数据的form表单 if request.method=='POST': # 包含用户提交数据的form和原始数据 form_obj=CustomerForm(request.POST,instance=obj) if form_obj.is_valid(): #如果校验成功,保存 form_obj.save() #跳转到展示页面 return redirect(reverse('customer_list')) else: # 包含原始数据的form表单 form_obj = CustomerForm(instance=obj) return render(request,'customer_edit.html',{'form_obj':form_obj})
原来的crm/url地址:
#添加客户 url(r'^customer_add/', customer.customer_add,name='customer_add'), #编辑客户 url(r'^customer_edit/(\d+)/', customer.customer_edit,name='customer_edit'), # url(r'^user_list/', views.user_list),
改变成一个url地址
处理这个一个后边有参数,一个后边没有参数,
将编辑功能的代码直接放到customer_change函数里边就修改完成了
如果添加点击这个,就说明了这个对象obj为空,也就是None,没有查找到这个,instance就是None
点击"CustomerForm"=>进入下列页面
再点击BSForm,进入
进入ModelForm
再走BaseModelForm,找到这个类里边的instance对象,默认就是None,也就相当于是没有数据
这时候就能添加成功了
现在存在的一个小问题是:
点击添加和编辑,得到的结果都含有"编辑客户这个字样",应该怎样处理?
在复制一份customer_edit.html页面,修改成customer_change.html
通过后端向前端传递这个title的值
这样就修改成功了
3.公户和私户的展示
区分这个问题,什么是公户,什么是私户?
公户:没有销售 consultant=null
私户:有固定的的销售 consultant=11 (等于一个具体的ID),不为空就是有销售
主要是看,客户表里边的字段:
也就是说,下面的这个"销售",就不应该在公户里边显示了,我们让当前的customer_list显示所有的公户,具体怎么处理呢?
现在,在公户里边,需要拿到的是没有销售的
这样查找的结果里边都是没有销售的了
运行:
下面我们修改下面的两个字段:
我们再修改前面的url
思考:公户和私户的区别是什么?
我们需要在customer.py里边做一个区分:根据什么区分?
也就是,我们需要判断路径
这样拿到的是下面的地址:crm/customer_list/通过request.path_info
我们通过反向解析,
现在,我们更换一下地址:
点击'客户库',拿到的是没有销售的,点击"我的客户",拿到的是"所有的客户"
销售应该拿到自己对应的销售名单,客户也是?我们首先要拿到当前的用户,首先要放到session里边,但是session能不能放对象?不能放
每次登陆都需要进行保存当前用户的id,所以我们需要保存在中间件中,我们创建文件夹middlewares,在下面创建auth.py
如果记不住,到settings.py里边找
通过找上边继承导入的包,进行处理
中间件的作用是什么?(可以去看下官网的解释)
官方解释 :在全局的范围内改变django的输入和输出!!!也就相当于一个钩子,在settings中注释掉,我们就不能用了.
也就是改变request对象和response对象
首先到大,WSGI(封装request对象)=>中间件(执行process_request方法)(多个中间件需要按照注册顺序执行)=>路由匹配=>视图函数=>执行中间件(process_view处理视图,做相应操作)
=>执行视图函数=>执行process_response方法(按照注册顺序,倒叙执行),最后给WSGI,WSGI再封装成httpresponse相应的相应格式,然后将字符串发送给浏览器,浏览器再解析拿到相应的结果
上边都是正常流程:
process_request方法,给process_view方法的返回值应该是什么?都是None,必须是None这个返回流程,才能正常走.
如果process_request返回一个httpresponse对象,这个流程应该怎样走?也就是下面的中间件process_response对象就不执行了,路由匹配也不执行了,视图函数也不执行了,也就是直接执行当前中间件的process_response方法,
然后,倒叙往回走,
如果是process_view返回一个httpresponse对象,这个流程应该怎样走?也就是后边的process_view对象就不执行了,视图函数也不执行了,执行走到最后一个中间件,执行process_response方法,正常往回走,按照倒叙执行这个process_response方法,
至少这三个的参数方法以及流程需要记清楚,
process_request,参数request对象,串联整个生命周期
process_view(request请求对象,路由匹配,视图函数,视图传递的位置参数和关键字参数) 这四个参数(路由匹配不算,只是一个过程)
process_response这个参数?(request对象,response对象),注意可能是各种形式传递过来的对象
两个特殊的:process_exception(处理异常),两个参数,request&&exception这个异常对象
process有个动词叫做处理请求,处理视图,处理响应
处理完异常之后,返回一个httpresponse对象,处理完异常之后,直接再执行process_response对象方法,如果没有处理完,返回的不是httpresponse对象,返回的是None对象,正常执行,上一个中间件中的process_response对象的process_exception方法
如果它能处理,后边不再走,走process_response方法,如果所有的中间件process_exception方法都没有处理这个异常,没有返回httpresponse对象的话,应该怎么办?这就相当于是自己就没有处理这个异常,这个异常交给django去处理,django会生成黄页的东西,
大黄页就是response对象,这个对象再交给process_reponse方法,再传递给浏览器,也就是异常会先自己处理,自己处理不了,在交给django去处理,
最后一个方法process_templates_response方法,
这个和processresponse有一点区别:需要有一个不能是普通的response对象了,必须是对象templatesresponse对象
下面进行验证一下:
返回上边这个方法,我们才会用process_templateresponse方法进行处理.好处是什么?
如果返回的是上边的对象,我们需要.
很少用,动态渲染,都渲染完了,才会用
也就是动态的处理模板,用的情况比较少,
高级用法:
也就是对象obj里边有一个render方法,
上边的这个用法也会触发,TemplateResponse方法,也是执行完所有的方法之后,才会执行obj后边的render方法
也就是拿到了return "x"
面试必问的问题:中间件
对比customer.py里边的用法:
拿到
django本身提供了一个auth模块的,不要起名叫user
这里边也会做认证做超级用户
所以,我们需要换个名字
上图就是修改之后的结果,在customer.py的用法:
注册这个中间件:
思考一下,重定向次数过多是什么原因引起的?
分析这个过程,先走crm里边的middlewares里边的auth.py里边创建的中间件.
需要进行判断一下:
这个时候,我们再看这个地址:
忘记账号和密码,在这里账户和密码不能添加.
ctrl+shift+delete=>等于是清除数据,google浏览器的清除缓存的快捷方式
自动跳转到login页面,如果输入的注册页面的话,所以注册这个页面也是需要放到白名单里边
注意,这个写法的变化
这样就解决了登录和注册的问题
但是admin也是有问题的 ,admin命名写不完,怎么办?
如果是admin开头的话,也是需要返回的
得到结果:
注意:如果
如果上图中的user_obj等于user,上边的admin界面是进不去的
账号是root,密码是root1234
账号和密码忘记可以翻看前面的blog
1. 目前存在的问题:输入一般的页面会弹到login页面,reg也就是登录页面可以找到
2.admin账号忘记了,看一下能不能找到,
3.其他页面不知道怎么会回事
现在实现的功能是必须是自己的账户才能看到用户和私户里边的信息,只用登录才能通过中间件
用户名:muye@qq.com
密码:123456
登陆之后才能够查看
上边admin登录不进去是因为账号和密码中的密码不正确,所以出现了问题:
账号是:root,密码是root123456,登录如下图所示:
登录之后的界面:
下面进行登录界面查看一下:
账户:alex@qq.com 密码:1234567
上图就是登录之后的界面
上图是客户库里边的信息,
上图是"我的客户"里边的信息.
寻找index的路径
上图是,从原来的直接返回一个字符串,到现在返回夜歌html页面,并且继承了layout.html页面
4.公户和私户的转换
上边进行了公私户展示,下面我们加上分页:
上图是"公户"和"私户"的展示页面:
最后返回的页面是customer_list.html
如何用分页,我们已经写成了类了?
上图的倒数第二行
需要对上图进行修改,在customer.py加上默认参数
然后,我们交给模板进行渲染
然后,我们直接放在模板里边
还没有得到安全的显示:前端加太麻烦,直接加在后端
在封装的类中加上mark_safe库
通过mark_safe包裹起来
再次运行,得到下面的结果:
会用到下面的信息:
放入母版的位置:
再处理一下:
这个时候就成功修改好了
公户里边也存在了页码
私户里边也存在了页码
如何做,公户和私户的转换?
公户变成自己的?如何实现批量的效果?加上checkbox
运行:
这个时候,就有了选择的效果:
我们可以加上复选框,进行选择,而不是按钮进行公户和私户的转换
将table放在a标签的下面:
如何实现这个功能?
现在有了select和下面的checkbox,我们需要通过form表单进行提交
先修改一下button的样式:
我们需要在上边的红框包上一层form,实现数据的提交
form-control指的是在一行放的好看一些
运行:
上边就是公户和私户的转换进行了修改
下面我们对form进行修改:
我们将form标签放在一行
我们需要知道的是,谁变成了谁,提交了哪些数据,这是我们需要知道的!!!!
加上一个action表示具体要提交的动作
选择也是需要进行对value起名字
select的action一提交,就相当于是拿到multi_apply或者multi_pub
下面,我们需要操作的是,具体操作的是哪些用户?我们需要拿到客户的id
这个时候,就有了下面的内容:
选择两个,F12点击下面的customer_list/,表示POST请求
上边我们看到的是提交的数据:
前端修改完成,需要修改后端的内容:两种不同的操作,需要做一些操作
如果除了有"公户变私户"还有更多的选择,应该怎么处理?
与字符串相关,可以想到什么?反射?
反射的概念:通过字符串的形式,操作类的属性或者类的方法,有四种:getattr,hasattr,setattr,delattr
下面我们通过反射的形式实现功能:
我们可以想办法把FBV写成CBV:
登录alex的账户之后
下面这个定义的是个CBV
我们需要修改一下url的地址:
这个时候路径修改成了CBV:不受影响了
运行:
选中之后,点击"提交"
因为在这个类中,我们没有定义POST方法,需要再定义一个POST方法
提交:
原因:现在我们通过反射拿不到相应的结果:
我们需要定义自己的方法:
根据value对应的值,写出相应的函数
下面,我们看一下as_views的流程:
点击进去
也就是说和现在的customer.py对应的是一个东西
下面开始拿东西
选中:
提交:
服务端,可以看到得结果:
我的操作就是这个结果,只能拿到一个,思考,该如何操作?
我们现在需要做的是,把客户表里边的"销售",进行修改
思考,如何修改?
使用update
将consultant="当前的用户"
每次通过,都会经过中间件middlewares里边的auth.py最下边的request.user_obj=user
因此我们可以直接写,下边的内容:
数据库存储的是_id(一个杠)
修改外键的方式:
全部选中:
提交:
相当于是最后return的是个None
不应该给None,应该给个direct
或者写下面的
两种写法都可以:
现在存在的问题是,一次只能加一个到私户
改回原来的样子需要在数据库中的customer里边修改consultant属性,改为空就可以了
当前,显示的关系是,多个客户对应一个用户,也就是多个客户关联1个销售,原来的用法是customer_set,现在我们指定了值,用上边指定的related_name="customers"
反向查询,add就类似于多对多的用法
选中1个,点击"提交"
说明这个地方写错了,括号内需要写对象,
点击"提交",可以成功,提交,但是只能提交1个结果
因此,我们可以用两种方式,进行添加
上图是,公户边私户的两种方式.
私户变公户的实现:
点击"提交",得到下图,成功实现
add修改成了,remove
点击提交后,我们可以在"公户"中查看到"私户"的内容了
现在的一个问题是,在点击"客户库"的时候,只显示"公户变私户",在"我的客户"的时候,显示"私户边公户",下面我们进行判断一下,进行处理
这个时候,在私户的页面里,只显示"私户变公户"
同理,在公户,只显示,私户变公户
有公共的再放在外边处理就可以了.
知识点:
ORM&&反射&&CBV&&路径判断&&每条数据加上checkbox
获取当前的action进行提交数据
获取整个列表,进行处理,上边如果用get的话,只能处理一条
反射+ORM实现(私户和公户的转换),以及一些html里边的相关操作.