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">&laquo;</span></a></li>')

    else:
        page_li_list.append(
            '<li ><a href="?page={}" aria-label="Previous"><span aria-hidden="true">&laquo;</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">&raquo;</span></a>')
    else:
        page_li_list.append(
            '<li ><a href="?page={}" aria-label="Next"><span aria-hidden="true">&raquo;</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">&laquo;</span></a></li>')
        # 页码不是1的都可以上翻页(bootstrap样式)
        else:
            page_li_list.append(
                '<li><a href="?page={}" aria-label="Next"><span aria-hidden="true">&laquo;</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">&raquo;</span></a></li>')
        # 页码不是最后页的都可以下翻页(bootstrap样式)
        else:
            page_li_list.append(
                '<li><a href="?page={}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                    self.page_num + 1))

        # 将列表内容拼接成一个字符串,也就是很多的li标签HTML代码 ,直接返回给前端
        return ''.join(page_li_list)
View Code

  实例化引用

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">&nbsp&nbsp&nbsp</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 %}
View Code

 

   

 

posted @ 2019-09-29 14:40  屈冠文  阅读(239)  评论(0编辑  收藏  举报