crm-2
1.分页
web必备的功能
1)批量制造测试数据
定义一个空列表用于存储 orm对象 ,models.表名(字段=...)创建orm对象append到列表 ,使用bulk_create(对象列表)一次性提交 ,避免了多次与数据库连接损耗
#脚本加载django环境 import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled.settings") import django django.setup() from crm import models from django.db import transaction DepObjList = [] for i in range(1,300): obj = models.Dep(name='部门{}'.format(i)) print(obj) DepObjList.append(obj) models.Dep.objects.bulk_create(DepObjList)
2)分页明确所需要的数据
后端分页的减轻数据库压力
per_num = 每页最大显示的页码数(自定义一个值如 20 50)
all_count = 一共有多少条数据需要展示(所有数据对象列表.count())
max_show = 底部页码栏展示的长度 (这里有左极值和右极值 ,以及访问页码时保证当前页码在中间显示)
half_show = 底部页码栏展示的一半是多少max_show // 2
page_num = 当前访问的页码 (根据该页码展示数据)
total_num = 页码的总数
start #根据当前访问的页码计算开始切片的索引
end #根据当前访问的页码计算结束切片的索引
page_html #生成底部页码的标签返回html页面
3)放在list视图函数中 ,因为前端的逻辑不太好写 ,所以将逻辑放在后端写 ,生成的标签通过mark_safe()转换为浏览器可解析的无需前端加safe
实现的点
(1)页码长度为固定值max_show ,且该值位于中心 ,左右有half_show的长度页码 (3 4 5 6 7 8 9 10 11 12 13 ) 选中8那么3-7和9-13就是half_show的长度!!
(2) 页码总数total_num小于max_show的情况下
(3)页码左极值不允许出现负值 ,如果按照中心定值那么一定会出现 (-2 -1 0 1 2 3 4 5 6 7 8 )选中3那么出现了负数页码 !! 改进当选中小于half_show的长度固定 1-max_show的页码
(4)页码右极值不允许出现超过total_num的值 ,如果按照中心定值会出现超出total_num的页码数 ! !改进当选中大于total_num-half_show ,长度固定 total_num减max_show - total_num页码
(5) 页码无极值问题情况下
(6) 每页根据当前页码 ,将对象列表切片显示本页码的数据
(7) 由于前段逻辑判断不好写, 放在后端写页码的li标签,其中当页码等于1或最后一页的时候需要禁止两侧按钮 ,放入列表拼接字符串转为安全代码传给前端展示
def deplist(request): all_obj = models.Dep.objects.all() per_num = 10 all_count = all_obj.count() max_show = 11 half_show = max_show // 2 # 当前访问的页面默认是1 ,如果url上有访问的page那么使用request.GET的参数 page_num = 1 if request.GET.get('page'): page_num = int(request.GET.get('page')) total_num = all_count // per_num # 最后一页的数据可能不够per_num那也需要展示 所以总页码数再加1 if all_count % per_num != 0: total_num += 1 """
限制底部页码长度 ,保证左右极值不会小于1 大于total_num ,并且保证长度 如点击标签8 ,那么保证8在中间 ,前后分别增加5个标签 ,《3 4 5 6 7 8 9 10 11 12 13》 """# 当页码不足定义好的最长的时候 ,有多长显示多长 if total_num < max_show: page_start = 1 page_end = total_num # 当页码足够多的时候 ,考虑左右极值的情况处理 else: # 左极值!如果访问页码小于 页码预设半长 ,那么直接显示1-max_show if page_num <= half_show: page_start = 1 page_end = max_show # 右极值!访问如果超过了total_num - half_show那么显示total_num-max_show到total_num这个固定长度 elif page_num > total_num - half_show: page_start = total_num - max_show+1 page_end = total_num # 无极值情况下 else: page_start = page_num - half_show page_end = page_num + half_show """ 切片(规律)的到start与end索引值 1页展示数据 msg:[0:30] 2页展示数据 msg:[30:60] ... page_num展示数据 msg:[(page_num - 1) * per_num : page_num * per_num] """ start = (page_num - 1) * per_num end = page_num * per_num page_li_list = [] if page_num == 1: page_li_list.append( '<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">«</span></a></li>') else: page_li_list.append( '<li ><a href="?page={}" aria-label="Previous"><span aria-hidden="true">«</span></a></li>'.format( page_num - 1 )) for i in range(page_start, page_end + 1): if i == page_num: page_li_list.append('<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)) else: page_li_list.append('<li><a href="?page={}">{}</a></li>'.format(i, i)) if page_num == total_num: page_li_list.append( '<li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a>') else: page_li_list.append( '<li ><a href="?page={}" aria-label="Next"><span aria-hidden="true">»</span></a>'.format(page_num + 1) ) page_html = ''.join(page_li_list) return render(request, 'deplist.html', {'msg': all_obj[start:end], 'page_html': mark_safe(page_html)})
2.分页使用类实现复用
定义一个类
####定义文件Pageination.py """ 使用方法: 在逻辑中引用实例化对象,一般来说page_num = request.GET.get('page',1) all_count=models...all().count() 在返回值中调用start与end方法获取到切片的索引! """ class Pageination: def __init__(self, page_num, all_count, per_num=20, max_show=11, ): # 当前访问页码值 try: self.page_num = int(page_num) except Exception as e: self.page_num = 1 # 数据总条数 self.all_count = all_count # 每页最大显示数据条数 self.per_num = per_num # 每页底部页码长度 self.max_show = max_show self.half_show = self.max_show // 2 # 计算页码总量 ,divmod方法 ,返回两个参数的 整除数 ,取余数,分别用两个参数接收 self.total_num, more = divmod(self.all_count, self.per_num) # total_num就是总共的页码数 if more != 0: self.total_num += 1 # 根据页码生成给数据切片的索引 @property # 方法调用变属性 def start(self): return (self.page_num - 1) * self.per_num @property # 方法调用变属性 def end(self): return self.page_num * self.per_num # 根据页码页码底部html标签 @property # 方法调用变属性 def page_html(self): # 限制起始值与终止值 ,当页码不足max_show ,那就直接将最大页码去显示 if self.total_num < self.max_show: page_start = 1 page_end = self.total_num else: # 左极值:防止负页码 ,当页码数小于5 就不要放把5中间了直接1-最大显示了 if self.page_num <= self.half_show: page_start = 1 page_end = self.max_show # 右极值:防止显示页码数大于页码总数 ,当某个页码数 ,该页码数的后面加上half_show超过了总页码数 elif self.page_num + self.half_show > self.total_num: # 我们将页码开始显示为总页码数减去每次显示的最大数 +1 获得了最后允许显示的开始值 page_start = self.total_num - self.max_show + 1 # 将最大页码数作为结束值 page_end = self.total_num else: page_start = self.page_num - self.half_show page_end = self.page_num + self.half_show # 将前端标签一个个追加到列表中(上翻-所有页码-下翻),加完之后统一生成一个str传到前端展示 page_li_list = [] # 上翻制作-本页码为1,则禁用(bootstrap样式) if self.page_num == 1: page_li_list.append( '<li class="disabled"><a href="" aria-label="Next"><span aria-hidden="true">«</span></a></li>') # 页码不是1的都可以上翻页(bootstrap样式) else: page_li_list.append( '<li><a href="?page={}" aria-label="Next"><span aria-hidden="true">«</span></a></li>'.format( self.page_num - 1)) # 将本次展示的页码<li>标签全部加入字典中 for i in range(page_start, page_end + 1): # 如果该页码就是访问的页码加上一个(bootstrap样式) if i == self.page_num: page_li_list.append('<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)) else: page_li_list.append('<li><a href="?page={}">{}</a></li>'.format(i, i)) # 下翻制作-点击页码为最后一个页码,则禁用(bootstrap样式) if self.page_num == self.total_num: page_li_list.append( '<li class="disabled"><a href="" aria-label="Next"><span aria-hidden="true">»</span></a></li>') # 页码不是最后页的都可以下翻页(bootstrap样式) else: page_li_list.append( '<li><a href="?page={}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format( self.page_num + 1)) # 将列表内容拼接成一个字符串,也就是很多的li标签HTML代码 ,直接返回给前端 return ''.join(page_li_list)
实例化引用
def deplist(request): all_obj = models.Dep.objects.all() pageobj = Pageination(request.GET.get('page', 1), all_obj.count()) return render(request, 'deplist.html', {'msg': all_obj[pageobj.start:pageobj.end], 'page_html': mark_safe(pageobj.page_html)})
3.crm数据补充
部门表,人员表的逻辑完成 , 新增班级表+课程表+校区表 ,其中班级表中外键课程和校区 ,多对多到人员表
问题1:多对多的表中 ,拥有mantomany字段的表会出现显示关系管理对象问题 解决1:在model中定义一个方法返回关系管理对象中所有对象名字 ,前端执行这个方法显示
问题2:前端显示时间的问题使用date: 'Y-m-d'解决
##models
class ClassList(models.Model): """班级表 其中任课老师与班级是多对多的""" ...... def __str__(self): return str(self.semester) def show_teachers(self): return ','.join([i.name for i in self.teachers.all()])
##html
<td>{{ obj.start_date|date:'Y-m-d' }}</td>
<td>{{ obj.graduate_date|date:'Y-m-d' }}</td>
<td>{{ obj.show_teachers }}</td>
补充班级相关增删改查逻辑 (edit与add整合为change)
#####url url(r'^class/list/', classes.classes_list, name='classes_list'), url(r'^class/add/', classes.classes_change, name='classes_add'), url(r'^class/edit/(\d+)', classes.classes_change, name='classes_edit'), url(r'^class/del/(\d+)', classes.classes_del, name='classes_del'), #####models class Course(models.Model): """课程名""" name = models.CharField(max_length=32) def __str__(self): return self.name class School(models.Model): """校区名""" name = models.CharField(max_length=32) def __str__(self): return self.name class ClassList(models.Model): """班级表 其中任课老师与班级是多对多的""" school = models.ForeignKey('School', verbose_name='校区名') course = models.ForeignKey('Course', verbose_name='课程表') semester = models.IntegerField(verbose_name='班级') price = models.IntegerField(verbose_name='价格') start_date = models.DateField(verbose_name='开班日期') graduate_date = models.DateField(verbose_name='结业日期', null=True, blank=True) tutor = models.ForeignKey(verbose_name='班主任', to='User', related_name='classes') teachers = models.ManyToManyField(verbose_name='任课老师', to='User', related_name='teach_classes') memo = models.CharField(verbose_name='说明', max_length=255, blank=True, null=True) def __str__(self): return str(self.semester) def show_teachers(self): return ','.join([i.name for i in self.teachers.all()]) #####modelform class ClassFrom(forms.ModelForm): class Meta: model = models.ClassList fields = '__all__' def __init__(self, *args, **kwargs): super(ClassFrom, self).__init__(*args, **kwargs) for field in self.fields.values(): print(field) field.widget.attrs.update({'class': 'form-control'}) #####view from django.shortcuts import render, reverse, redirect, HttpResponse from crm import models from crm.modleforms import ClassFrom def classes_list(request): all_class = models.ClassList.objects.all() return render(request, 'classes_list.html', {'msg': all_class}) def classes_change(request, edit_id=None): obj = models.ClassList.objects.filter(pk=edit_id).first() form_obj = ClassFrom(instance=obj) if request.method == 'POST': form_obj = ClassFrom(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('crm:classes_list')) return render(request, 'depadd-edit.html', {'form_obj': form_obj}) def classes_del(request, del_id): obj = models.ClassList.objects.filter(pk=del_id).delete() return redirect(reverse('crm:classes_list')) #####edit.hrml {% extends 'layout.html' %} {% block content %} <div class="container col-lg-4 col-md-offset-3" style="margin-top: 30px;"> <form class="form-horizontal" method="post" novalidate> {% csrf_token %} {% for obj in form_obj %} <div class="form-group {% if obj.errors %}has-error{% endif %}"> <label for="{{ obj.id_for_label }}" class="col-sm-2 control-label">{{ obj.label }}</label> <div class="col-sm-10"> {{ obj }} <span class="help-block has-error">{{ obj.errors.0 }}</span> </div> </div> {% endfor %} <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default">保存</button> </div> </form> </div> {% endblock %} #####list.html {% extends 'layout.html' %} {% load static %} {% block js %} <script src="{% static '/js/sweetalter.js' %}"></script> <script> $('.b1').click(function () { swal({ title: "提示", text: "删除后无法恢复", icon: "warning", buttons: true, dangerMode: true, }) .then((willDelete) => { let del_id = $(this).attr('del_id'); if (willDelete) { $.ajax({ url: '/crm/dep/del/' + del_id, type: 'get', success: () => { swal("已删除!", { icon: 'success', }); $(this).parent().parent().remove() } }); } else { swal("取消删除!"); } }); }) </script> {% endblock %} {% block content %} <table class="text-center table table-striped table-bordered" style="margin-top: 20px"> <tr> <td>序号</td> <td>id</td> <td>部门</td> <td>描述</td> <td>操作</td> </tr> {% for obj in msg %} <tr> <td>{{ forloop.counter }}</td> <td>{{ obj.pk }}</td> <td>{{ obj.name }}</td> <td>{{ obj.desc }}</td> <td><a href={% url 'crm:depedit' obj.pk %}><i class="fa fa-pencil-square-o" aria-hidden="true">   </i></a> <a class="b1" del_id="{{ obj.pk }}" style="color: red"><i class=" fa fa-remove" aria-hidden="true"></i></a></td> </tr> {% endfor %} </table> <nav aria-label="Page navigation"> <ul class="pagination"> {{ page_html }} </ul> </nav> {% endblock %}