Django实战【四】—客户管理模块代码实现
一、客户管理系统增删改查
1.业务回顾
大家还记得我们项目的三大块吧,第一个就是客户管理。
客户管理系统是提供给网咨、销售人员来查看客户信息、增加客户、修改客户信息的。当然客户在我们项目中还有公户和私户的区别,公户私户实际是由销售总监根据销售的业务能力来合理分配,从而使客户转化率最大化。
销售人员通过分配到的客户,存放在自己的私户中,通过不断的跟进,交流,让客户报名。期间的跟进记录我们应当都存入数据库,以便以后的继续跟进。
由于该博客仅用于知识点的分享实现,所以我们这里只实现一个完整的功能块,其他类似的就不展示了,所以我们这里只实现客户的信息增删改查,也就是customer应用下的客户数据。
2.公共客户数据的增删改查url
项目下urls路由分发对应的请求到customer应用下
url(r'^customer/', include("customer.urls")),
customer应用下的增删改查url
from django.conf.urls import url from customer.views import customer urlpatterns = [ # 公户数据展示url url(r'^common/list', customer.CommonList.as_view(), name="common_list"), # 公户信息添加url url(r'^common/add/', customer.CommonAdd.as_view(), name="common_add"), # 公户信息修改url url(r'^common/edit/(\d+)/', customer.CommonEdit.as_view(), name="common_edit"), # 公户信息删除 url(r'^common/del/(\d+)/', customer.CommonDel.as_view(), name="common_del"), ]
3.公户数据的展示视图和模板
关于客户信息的相关视图我们放在customer下的views文件夹下的customer.py中。
公户信息展示几点注意点:
- 在客户数据展示的视图中我们使用了django自带的auth认证装饰器,装饰器在验证失败后会跳转login页面,但是django默认配置的跳转login页面url并不是我们想要的,需要我们在settings中配置一下。
# 配置登录认证失败跳转的页面 LOGIN_URL = '/crmweb/login/'
- 在数据展示页面中,还使用了自定义分页,自定义分页博客中有,就不详述了,想要查看点这里:Django框架—分页器paginator 过滤器部分
公户展示视图写法
公户信息展示中实现了两个额外功能
- 查询功能
查询功能的实现,是基于form表单通过get提交select框和input框,select框用来提交查询的条件,input框提交查询的查询参数。
后端从get请求中获取前端参数,根据select提交的值来确定通过什么字段来查询,根据input的值来确定查询条件。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<form action="" method="get" class="navbar-form navbar-left"> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn input-group-sm btn-info" style="border: 0"> <option value="" readonly>条件</option> <option value="qq_name">昵称</option> <option value="qq">QQ号</option> </select> </div> <input type="text" name="q" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <button type="submit" id="search-btn" class="btn btn-flat"> <i class="fa fa-search"></i> </button> </span> </div> </form>
- 批量操作功能
批量操作功能的实现,也是通过form表单post请求提交数据,在类中定义一系列批量操作的方法。
根据前端select提交的option选项中的value值,也就是字符串来反射类中的批量操作方法,根据表格中提交的记录id来批量修改。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django import views from customer import models from django.db.models import Q, Count from django.shortcuts import ( render, redirect, reverse, HttpResponse ) from customer.forms import formAuth from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator from utils.customPaginator import Paginator # 公户数据展示 class CommonList(views.View): @method_decorator(login_required) # 装饰器函数验证是否登录 def dispatch(self, request, *args, **kwargs): res = super().dispatch(request, *args, **kwargs) return res def get(self, request): # 查询公户全部数据 all_customers = models.Customer.objects.filter(consultant__isnull=True, status="unregistered").order_by( "-pk") # 使用Q查询拼接查询条件 condition = request.GET.get("condition", "") # 获取搜索的条件分类 query = request.GET.get("q", "") # 获取搜索的条件 if condition and query: # 如果有查询调参数,两个参数都有,根据查询参数查询后找到数据 condition = condition + "__contains" q = Q() # Q实例化生成q对象,q对象可以帮我们拼接字符串为 condition__contians= xx的关键字参数传到filter中。 q.children.append((condition, query)) all_customers = all_customers.filter(q) # 开始分页展示 data_counts = all_customers.count() # 获取分页的总数据数量 # 生成一个分页对象 paginator = Paginator(request, data_counts, 10) # 获取当前页展示数据的范围 try: # 异常是否查到了数据,查到了才切片,不然会报错 all_customers = all_customers[paginator.start:paginator.end] except Exception: pass # 获取分页的标签 paginator_tag = paginator.paginate() # 调用定义好的分页方法 # 获取跳转页的标签 jump_tag = paginator.jump_page() # 调用定义好的跳转页方法获取跳转页标签 jump_js = paginator.jump_js() # 调用定义好的跳转页方法获取跳转页js代码 # fixme 这里我实现的用户被选走的提示方式有点low,暂时先这样吧,而且在实际业务中,公户转私户应该是由销售总监分配的,而不是比谁先抢到。 name_str = None # 客户被选走的错误提示 if "*customer*" in request.path: name_list = request.path.split("*customer*")[1:] name_str = ','.join(name_list) # 返回response对象,以及需要渲染的数据 return render(request, "common_list.html",{"all_customers": all_customers, "paginator_tag": paginator_tag,"jump_tag": jump_tag, "jump_js": jump_js, "name_str": name_str}) def post(self, request): operate = request.POST.get("operate") # 获取用户提交的批量操作类型 if operate: # 如果有,去反射类中对应的批量操作方法 if hasattr(self, operate): func = getattr(self, operate) if callable(func): ret = func(request) # 执行批量操作方法 if ret: # 函数有返回值,也就是有被别的销售提前选走的客户 info = "" for obj in ret: # fixme 客户被任选走,在前端提示哪些客户被选走,这里我放在路径中,并不太好,目前就这样实现,以后有更好的办法再更新 info = info + "*customer*" + obj.__str__() url = request.path + info # 拼接url,携带提示信息 return redirect(url) return redirect(request.path) else: return HttpResponse("访问连接有误!") return HttpResponse("访问连接有误!") return redirect("common") def batch_delete(self, request, *args, **kwargs): """批量删除客户""" # 实际工作场景中并不是真的删除,而是修改该条数据在数据库的修改状态 choose_list = request.POST.getlist("choose") models.Customer.objects.filter(pk__in=choose_list).delete() def batch_update(self, request, *args, **kwargs): """批量更新客户状态""" choose_list = request.POST.getlist("choose") models.Customer.objects.filter(pk__in=choose_list).update(status="studying") def batch_c2p(self, request, *args, **kwargs): """批量公户转私户操作""" choose_list = request.POST.getlist("choose") # 获取选中的客户id,注意通过getlist来获取,获取一个列表 customer_list = models.Customer.objects.filter(pk__in=choose_list) # 根据客户id查到客户 has_choosed = [] # 定义一个列表 for customer_obj in customer_list: if customer_obj.consultant: # 如果客户被别人选了,放到已选列表 has_choosed.append(customer_obj) else: # 如果还没有备选则选择并保存 customer_obj.consultant = request.user customer_obj.save() # 通过save方法保存到数据库 return has_choosed # 返回已经被选择的用户
base.html
我们之前说过项目模板是用的开源的模板,这里我们是使用一个base页面,其他页面结构相同的都来继承这个base页面。
话说还记得模板继承的用法把,这里简单回顾:
先提取公共页面到一个base.html文件中,对于需要根据具体页面内容个性化定制的地方,使用block块来包裹,block块尽量多。
在继承的页面中,通过extends来继承base页面,对于需要自己定制的部分,在block中写入自己的内容。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% load static %} <!DOCTYPE html> <!-- This is a starter template page. Use this page to start your new project from scratch. This page gets rid of all links and provides the needed markup only. --> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AliCRM</title> {% block head %} <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- AdminLTE Skins. We have chosen the skin-blue for this starter page. However, you can choose any other skin. Make sure you apply the skin class to the body tag so the changes take effect. --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/skins/skin-blue.min.css' %}"> {% endblock head %} </head> <!-- BODY TAG OPTIONS: ================= Apply one or more of the following classes to get the desired effect |---------------------------------------------------------| | SKINS | skin-blue | | | skin-black | | | skin-purple | | | skin-yellow | | | skin-red | | | skin-green | |---------------------------------------------------------| |LAYOUT OPTIONS | fixed | | | layout-boxed | | | layout-top-nav | | | sidebar-collapse | | | sidebar-mini | |---------------------------------------------------------| --> <body class="hold-transition skin-blue sidebar-mini"> <div class="wrapper"> <!-- Main Header --> <header class="main-header"> <!-- Logo --> <a href="" class="logo"> <!-- mini logo for sidebar mini 50x50 pixels --> <span class="logo-mini"><b>A</b>li</span> <!-- logo for regular state and mobile devices --> <span class="logo-lg"><b>Ali</b>CRM</span> </a> <!-- Header Navbar --> <nav class="navbar navbar-static-top" role="navigation"> <!-- Sidebar toggle button--> <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button"> <span class="sr-only">Toggle navigation</span> </a> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse pull-left" id="navbar-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu" role="menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li class="divider"></li> <li><a href="#">Separated link</a></li> <li class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> </div> <!-- Navbar Right Menu --> <div class="navbar-custom-menu"> <ul class="nav navbar-nav"> <!-- Messages: style can be found in dropdown.less--> <li class="dropdown messages-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-envelope-o"></i> <span class="label label-success">4</span> </a> <ul class="dropdown-menu"> <li class="header">You have 4 messages</li> <li> <!-- inner menu: contains the messages --> <ul class="menu"> <li><!-- start message --> <a href="#"> <div class="pull-left"> <!-- User Image --> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> </div> <!-- Message title and timestamp --> <h4> Support Team <small><i class="fa fa-clock-o"></i> 5 mins</small> </h4> <!-- The message --> <p>Why not buy a new awesome theme?</p> </a> </li> <!-- end message --> </ul> <!-- /.menu --> </li> <li class="footer"><a href="#">See All Messages</a></li> </ul> </li> <!-- /.messages-menu --> <!-- Notifications Menu --> <li class="dropdown notifications-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-bell-o"></i> <span class="label label-warning">10</span> </a> <ul class="dropdown-menu"> <li class="header">You have 10 notifications</li> <li> <!-- Inner Menu: contains the notifications --> <ul class="menu"> <li><!-- start notification --> <a href="#"> <i class="fa fa-users text-aqua"></i> 5 new members joined today </a> </li> <!-- end notification --> </ul> </li> <li class="footer"><a href="#">View all</a></li> </ul> </li> <!-- Tasks Menu --> <li class="dropdown tasks-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-flag-o"></i> <span class="label label-danger">9</span> </a> <ul class="dropdown-menu"> <li class="header">You have 9 tasks</li> <li> <!-- Inner menu: contains the tasks --> <ul class="menu"> <li><!-- Task item --> <a href="#"> <!-- Task title and progress text --> <h3> Design some buttons <small class="pull-right">20%</small> </h3> <!-- The progress bar --> <div class="progress xs"> <!-- Change the css width attribute to simulate progress --> <div class="progress-bar progress-bar-aqua" style="width: 20%" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"> <span class="sr-only">20% Complete</span> </div> </div> </a> </li> <!-- end task item --> </ul> </li> <li class="footer"> <a href="#">View all tasks</a> </li> </ul> </li> <!-- User Account Menu --> <li class="dropdown user user-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <!-- The user image in the navbar--> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="user-image" alt="User Image"> <!-- hidden-xs hides the username on small devices so only the image appears. --> <span class="hidden-xs">{{ request.user.username }}</span> </a> <ul class="dropdown-menu"> <!-- The user image in the menu --> <li class="user-header"> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> <p> {{ request.user.username }} - Web Developer <small>Member since Nov. 2012</small> </p> </li> <!-- Menu Body --> <li class="user-body"> <div class="row"> <div class="col-xs-4 text-center"> <a href="#">Followers</a> </div> <div class="col-xs-4 text-center"> <a href="#">Sales</a> </div> <div class="col-xs-4 text-center"> <a href="#">Friends</a> </div> </div> <!-- /.row --> </li> <!-- Menu Footer--> <li class="user-footer"> <div class="pull-left"> <a href="" class="btn btn-default btn-flat">Profile</a> </div> <div class="pull-right"> <a href="" class="btn btn-default btn-flat">Sign out</a> </div> </li> </ul> </li> <!-- Control Sidebar Toggle Button --> <li> <a href="#" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a> </li> </ul> </div> </nav> </header> <!-- Left side column. contains the sidebar --> <aside class="main-sidebar"> <!-- sidebar: style can be found in sidebar.less --> <section class="sidebar"> <!-- Sidebar user panel --> <div class="user-panel"> <div class="pull-left image"> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> </div> <div class="pull-left info"> <p>{{ request.user.username }}</p> <a href="#"><i class="fa fa-circle text-success"></i> Online</a> </div> </div> <!-- search form --> <form action="#" method="get" class="sidebar-form"> <div class="input-group"> <input type="text" name="q" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i> </button> </span> </div> </form> <!-- /.search form --> <!-- sidebar menu: : style can be found in sidebar.less --> <ul class="sidebar-menu" data-widget="tree"> <li class="header">操作菜单</li> <li> <a href="{% url 'common_list' %}"> <i class="fa fa-cubes"></i> <span>公户信息展示</span> <span class="pull-right-container"></span> </a> </li> </ul> </section> <!-- /.sidebar --> </aside> <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> {% block title %} Title {% endblock title %} <small>Developer Ryxiong</small> </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> Level</a></li> <li class="active">Here</li> </ol> </section> <!-- Main content --> <section class="content container-fluid"> <!-------------------------- | Your Page Content Here | --------------------------> {% block content %} Content {% endblock content %} </section> <!-- /.content --> </div> <!-- /.content-wrapper --> <!-- Main Footer --> <footer class="main-footer"> <!-- To the right --> <div class="pull-right hidden-xs"> Anything you want </div> <!-- Default to the left --> <strong>Copyright © 2016 <a href="#">Company</a>.</strong> All rights reserved. </footer> <!-- Control Sidebar --> <aside class="control-sidebar control-sidebar-dark"> <!-- Create the tabs --> <ul class="nav nav-tabs nav-justified control-sidebar-tabs"> <li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li> <li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li> </ul> <!-- Tab panes --> <div class="tab-content"> <!-- Home tab content --> <div class="tab-pane active" id="control-sidebar-home-tab"> <h3 class="control-sidebar-heading">Recent Activity</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <i class="menu-icon fa fa-birthday-cake bg-red"></i> <div class="menu-info"> <h4 class="control-sidebar-subheading">Langdon's Birthday</h4> <p>Will be 23 on April 24th</p> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> <h3 class="control-sidebar-heading">Tasks Progress</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <h4 class="control-sidebar-subheading"> Custom Template Design <span class="pull-right-container"> <span class="label label-danger pull-right">70%</span> </span> </h4> <div class="progress progress-xxs"> <div class="progress-bar progress-bar-danger" style="width: 70%"></div> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> </div> <!-- /.tab-pane --> <!-- Stats tab content --> <div class="tab-pane" id="control-sidebar-stats-tab">Stats Tab Content</div> <!-- /.tab-pane --> <!-- Settings tab content --> <div class="tab-pane" id="control-sidebar-settings-tab"> <form method="post"> <h3 class="control-sidebar-heading">General Settings</h3> <div class="form-group"> <label class="control-sidebar-subheading"> Report panel usage <input type="checkbox" class="pull-right" checked> </label> <p> Some information about this general settings option </p> </div> <!-- /.form-group --> </form> </div> <!-- /.tab-pane --> </div> </aside> <!-- /.control-sidebar --> <!-- Add the sidebar's background. This div must be placed immediately after the control sidebar --> <div class="control-sidebar-bg"></div> </div> <!-- ./wrapper --> {% block js %} <!-- REQUIRED JS SCRIPTS --> <!-- jQuery 3 --> <script src="{% static 'jquery/jquery-3.4.1.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> <!-- AdminLTE App --> <script src="{% static 'adminlte/dist/js/adminlte.min.js' %}"></script> <!-- Optionally, you can add Slimscroll and FastClick plugins. Both of these plugins are recommended to enhance the user experience. --> {% endblock js %} {% block customjs %} {% endblock customjs %} </body> </html>
公户信息展示html
公户信息展示继承base页面
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% extends 'BASE.html' %} {% load static %} {% block head %} {{ block.super }} {% endblock head %} {% block title %} 公户信息展示 {% endblock title %} {% block content %} <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> <h3 class="box-title"></h3> <form action="" method="get" class="navbar-form navbar-left"> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn input-group-sm btn-info" style="border: 0"> <option value="" readonly>条件</option> <option value="qq_name">昵称</option> <option value="qq">QQ号</option> </select> </div> <input type="text" name="q" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <button type="submit" id="search-btn" class="btn btn-flat"> <i class="fa fa-search"></i> </button> </span> </div> </form> <a href="{% url 'common_add' %}" class="btn btn-primary pull-right">新增客户</a> </div> <div class="box-body"> <div class="row"> <div class="col-sm-6"> </div> </div> <form action="" method="post"> {% csrf_token %} <div class="input-group" style="width: 220px;margin-bottom: 5px;margin-left: 15px"> <select name="operate" id="operate" class="form-control btn-default"> <option value="">选择批量操作</option> <option value="batch_delete">批量删除</option> <option value="batch_update">批量更改客户状态</option> {% if flag %} <option value="batch_c2p">批量公转私</option> {% else %} <option value="batch_c2p">批量公转私</option> {% endif %} </select> <span class="input-group-btn"> <button type="submit" class="btn btn-warning btn-flat">Go!</button> </span> </div> {% if name_str %} <div class="btn text-danger" id="choose_error">顾客:{{ name_str }}已经被选走了</div> {% endif %} <table id="example2" class="table table-bordered table-hover text-center"> <thead> <tr> <th style="width: 6%"> <span> <i class="fa fa-check-square-o"></i> <input type="checkbox" name="batch_choose"> </span> </th> <th style="width: 5%">序号</th> <th>qq</th> <th>姓名</th> <th>电话</th> <th>来源</th> <th>咨询课程</th> <th>客户状态</th> <th>销售老师</th> <th>操作</th> </tr> </thead> <tbody> {% for customer in all_customers %} <tr> <td><input type="checkbox" name="choose" value="{{ customer.pk }}"></td> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> <td>{{ customer.qq_name }}</td> <td> {{ customer.phone|default:"暂无" }} </td> <td>{{ customer.get_source_display|default:'暂无' }}</td> <td>{{ customer.get_course_display|default:"暂无" }}</td> <td>{{ customer.get_status_display }}</td> <td>{{ customer.consultant.username|default:'暂无' }}</td> <td> <a style="color: #00c3cc;" href="{% url 'common_edit' customer.pk %}"> <i class="fa fa-edit" aria-hidden="true"></i> </a> | <a style="color: #d9534f;" href="{% url 'common_del' customer.pk %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% endfor %} </tbody> <tfoot> </tfoot> </table> {% if not all_customers %} <h3 class="text-center">没有相关记录!</h3> {% endif %} </form> <div class="pull-right" style="display:inline-block; width: 120px;margin: 22px 10px"> {{ jump_tag|safe }} </div> <div class="pull-right"> {{ paginator_tag|safe }} </div> </div> <!-- /.box-body --> </div> <!-- /.box --> </div> <!-- /.col --> </div> {% endblock content %} {% block js %} {{ block.super }} {% endblock js %} {% block customjs %} <script> $("[name=batch_choose]").click(function () { var status = $(this).prop("checked"); $("[name=choose]").prop('checked', status) }); $("#choose_error").click(function () { $("#choose_error").css("display", "none"); }) </script> {{ jump_js|safe }} {% endblock customjs %}
实现后的页面效果
4.添加公户数据的视图和模板
添加公户视图函数
公户数据的添加,我们这里是通过modelform组件写的,所以先在customer应用下forms文件夹下formAuth.py中定义一个ModelForm。
from django import forms from customer import models # 顾客添加form认证 class CustomerAddMF(forms.ModelForm): # 定义添加数据使用的ModelForm class Meta: model = models.Customer # 指定一张表 fields = "__all__" # 指定字段,排除用exclude def __init__(self,*args,**kwargs): """重写init方法,批量添加标签样式""" super().__init__(*args,**kwargs) # 执行父类的init方法,必须 for field in self.fields: if field != "course": # 课程是多选框,不设置标签样式 self.fields[field].widget.attrs.update({"class":"forms-control",})
添加公户视图写法:
添加公户是通过modelform来实现的,get请求根据modeform实例化对象,返回前端,前端遍历对象来生成标签。post请求根据后端提交的数据给modelform去实例化,然后验证数据合法性,合法后才写入数据库。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 添加公共客户记录 class CommonAdd(views.View): """添加公户记录视图""" @method_decorator(login_required) # 登录验证 def dispatch(self, request, *args, **kwargs): res = super().dispatch(request, *args, **kwargs) return res def get(self, request): form_obj = formAuth.CustomerAddMF() # 通过modelform实例化生成对象 return render(request, "common_add.html", {"form_obj": form_obj}) def post(self, request): # 实例化modelform并传入前端提交的数据 form_obj = formAuth.CustomerAddMF(request.POST) if form_obj.is_valid(): # 对提交数据验证 form_obj.save() # 合法保存到数据库 return redirect("common_list") else: # 不合法将原有数据返回给页面,并显示错误提示 return render(request, "common_add.html", {"form_obj": form_obj})
添加公户html文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% extends 'BASE.html' %} {% block head %} {{ block.super }} {% endblock head %} {% block title %} 新增客户 {% endblock title %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-sm-7"> <form action="" method="post" class="form-horizontal" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group"> <label for="{{ field.id_for_label }}" class="control-label col-sm-2">{{ field.label }}</label> <div class="col-sm-8"> {{ field }} <span class="text-danger">{{ field.errors.0 }}</span> </div> </div> {% endfor %} <input type="submit" class="btn btn-success pull-right" value="添加"> </form> </div> </div> </div> {% endblock content %} {% block js %} {{ block.super }} {% endblock js %}
5.编辑客户的视图和模板
编辑客户视图写法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 修改公共客户记录 class CommonEdit(views.View): @method_decorator(login_required) # 登录验证状态 def dispatch(self, request, *args, **kwargs): res = super().dispatch(request, *args, **kwargs) return res def get(self, request, n): """ get请求,展示修改页面,其中展示原始数据 :param request: request请求 :param n: 前端传递过来的记录id :return: 返回response对象 """ customer_obj = models.Customer.objects.filter(pk=n).first() # 找到需要修改的客户对象 form_obj = formAuth.CustomerAddMF(instance=customer_obj) # 通过modelform实例化的instance参数指定一个客户对象,可以在前端页面中直接渲染原始数据。 return render(request, "common_add.html", {"form_obj": form_obj}) def post(self, request, n): """修改客户记录post请求""" customer_obj = models.Customer.objects.filter(pk=n).first() # 通过modelform实例化,传递页面提交的post数据,指定instance实例 form_obj = formAuth.CustomerAddMF(request.POST, instance=customer_obj) if form_obj.is_valid(): form_obj.save() # 合法后保存,注意如果实例化时,没有传实例,是创建记录,指定了实例才是修改数据 return redirect("common") else: # 数据不合法,保留页面和原始数据,并给出错误提示 return render(request, "common_add.html", {"form_obj": form_obj})
修改客户记录html文件
modelform组件有一个功能,就是在实例化的时候,指定instance的对象,可以直接获取对象的值,并在页面中渲染出来,不需要我们再通过render单独传递数据渲染了。所以我们修改数据的页面的html文件与添加的页面没有差别,这里就直接使用上面common_add.html文件即可。
其次,在post请求提交数据后,modelform实例化对象的时候,如果直接传递request.post数据,通过modelform对象.save方法可以新建数据;如果指定了instance为哪个记录对象,save方法就是修改这个记录对象的数据。
6.删除客户的视图和模板
删除客户数据视图
# 删除公共客户记录 class CommonDel(views.View): @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): res = super().dispatch(request, *args, **kwargs) return res def get(self, request, n): # 根据前端传递的客户id,查找客户对象 customer_obj = models.Customer.objects.filter(pk=n) customer_obj.delete() # fixme 对象的delete方法删除,其实实际工作中不应是真的删除,而是修改该记录的删除状态为True,即可。 return redirect("common_list")
删除数据不需要页面展示,其实我们本应该在删除前有一个确认过程,而不是点击一下,就直接删除,这样可能会导致误删。至于确认删除的过程,我们可通过js代码来实现,这里我就不详细写了。