第五十八篇 Django-CRM系统-2客户相关功能,增删改查,分页,模糊搜索,保留路径,批量操作

一、CRM框架

在这里插入图片描述

二、展示信息

class Show_Customer(View):

    def get(self, request):
        if request.path_info == "/crm/":
            all_customer = models.Customer.objects.filter(consultant__isnull=True)
        else:
            if request.path_info == "/crm/all_customer/":  # 所有客户
                all_customer = models.Customer.objects.all()
            else:
                all_customer = models.Customer.objects.filter(onsultant=request.user)  # 我的客户
        return render(request, 'crm/customer.html',
                      context={"all_customer": all_customer})

    def post(self, request):
   		pass

将all_customer的参数传递给前端,前端拿出数据再进行显示
choice字段才有的显示,定义显示用户需要看到的字段,get_字段_display,在python文件中需要用get_字段_display()

{% for customer in all_customer %}
        <tr>
            <td><input type="checkbox" name="id" value="{{ customer.id }}"></td>
            <td>{{ forloop.counter }}</td>
            <td>{{ customer.qq }}</td>
            <td>{{ customer.qq_name|default:'暂无' }}</td>  #定义默认值
            <td>{{ customer.name|default:'暂无' }}</td>
            <td>{{ customer.get_sex_display }}</td>
            <td>{{ customer.phone|default:'暂无' }}</td>
            <td>{{ customer.get_source_display }}</td>   
            <td>{{ customer.get_class_type_display }}</td>
            <td>
                {{ customer.show_status }}#也可以使用models中定义的函数
            </td>
{% endfor %}

在models中定义函数,用于显示当前状态

from django.utils.safestring import mark_safe  # 将html内容返回到前端时,用此方法
    def show_status(self):
        color_dict = {
            "signed": 'green',
            "unregistered": 'red',
            "studying": 'pink',
            "paid_in_full": 'blue',

        }
        return mark_safe(
            '<span style="background-color: {};color: white;padding: 4px">{}</span>'.format(color_dict[self.status],
                                                                                            self.get_status_display()))

三、新增信息

class CustomerForm(forms.ModelForm):
    class Meta:
        model = models.Customer
        fields = '__all__'
def add_customer(request):
    customer_obj = CustomerForm()
    if request.method == "POST":
        customer_obj = CustomerForm(request.POST)
        if customer_obj.is_valid():
            customer_obj.save()
            # save方法直接将清洗过的数据存放到数据库中
            print(customer_obj.cleaned_data)
            return redirect(reverse('customer'))
    return render(request, 'crm/add_customer.html', context={"customer_obj": customer_obj})

前端循环
通过Form传递过来的字段可以传给前端页面,进行循环逐一获取
label: field.label
label-for : field.id_for_bale
errors: field.errors
字段: field

<form action="" method="post" novalidate>
                    {% csrf_token %}
                    {% for field in customer_obj %}

                        <div class="form-group row {% if field.errors %}has-error{% endif %} ">

                            <label for="{{ field.id_for_label }}"
                                   class="col-sm-2 control-label"> {{ field.label }}</label>
                            <div class="col-sm-10">
                                {{ field }}
                                <span class="help-block">
                            {{ field.errors.0 }}
                        </span>
                            </div>
                        </div>

                    {% endfor %}
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                            <button type="submit" class="btn btn-primary">提交</button>
                        </div>
                    </div>

                </form>

四、编辑信息

与新增的代码相似,所以可以直接使用将新增和编辑代码合并,将传入的参数赋值为None就可以成功合并

# 编辑客户
def edit_customer(request, customer_id):
    # 参数是通过前端编辑a标签传递customer_id,用id来获取当前信息,并通过form表单实例化,修改完之后再保存下来
    obj = models.Customer.objects.filter(id=customer_id).first() #通过id获取客户对象
    customer_obj = CustomerForm(instance=obj)
    if request.method == "POST":
        customer_obj = CustomerForm(request.POST, instance=obj)
        if customer_obj.is_valid():
            customer_obj.save()
            next = request.GET.get('next')
            if next:
                return redirect(next)
            return redirect(reverse('customer'))
    return render(request, 'crm/edit_customer.html', context={"customer_obj": customer_obj})

五、分页功能的实现

  1. 分页功能的基本实现
# 分页练习
users = [{"name": "tian{0}".format(i), "pwd": "pwd{0}".format(i)} for i in range(302)]

def page(request):
    #获取路径
    path = request.path_info
    # print(path,request)
    # 获得当前页码数
    current = request.GET.get('page', 1)
    page = int(current)
    per_pagination = 11  # 显示页码数
    half = per_pagination // 2
    per_page = 10  # 每页显示的数据个数

    # 获取当前页应该对应的页面数据
    pre = (page - 1) * per_page
    next = page * per_page
    print(users[pre:next])
    # 1 0 9
    # 2 10 19
    # 3 20 29
    # 4 30 39
    # 5 40 49

    # 通过当前页来传递页的数字
    num_page, remainder = divmod(len(users), per_page)
    if remainder:
        num_page += 1

    pagination = [str(i) for i in range(1, num_page + 1)]  # 页码数据列表

    if page > 0 and page < 6:
        start = 1
        end = per_pagination
    elif page > 26:
        start = num_page - per_pagination + 1
        end = num_page
    else:
        start = page - half
        end = page + half

    #     # 存放li标签的列表
    html_list = []
    #首页
    first_li = '<li><a href="{}?page=1">首页</a></li>'.format(path)
    html_list.append(first_li)
    #上一页
    if page == 1:
        prev_li = '<li class="disabled"><a><<</a></li>'
    else:
        prev_li = '<li><a href="{1}?page={0}"><<</a></li>'.format((page - 1),path)
    html_list.append(prev_li)
    #中间显示页码数
    for p in pagination[start - 1:end]:
        page_li = '<li><a href="{1}?page={0}">{0}</a></li>'.format(p,path)
        html_list.append(page_li)
    #下一页
    if page == num_page:
        next_li = '<li class="disabled"><a>>></a></li>'
    else:
        next_li = '<li><a href="{1}?page={0}">>></a></li>'.format((page + 1),path)
    html_list.append(next_li)
    #尾页
    last_li = '<li><a href="{1}?page={0}">尾页</a></li>'.format(num_page,path)
    html_list.append(last_li)

    html_str = mark_safe(''.join(html_list))
    return render(request, 'crm/page.html',
                  context={"users": users[pre:next], "pagination": pagination[start - 1:end], "html_str": html_str}
  1. 分页功能写成类,调用起来会很方便

query_params参数为当前页面的request.GET请求,需要将GET中的路径信息,将其加入到分页中,实现了翻页中保留搜索条件

from django.utils.safestring import mark_safe  # 安全传输html标签去前端
from django.http import QueryDict  # request.GET返回的是QueryDict


class Page():
    #SyntaxError: non-default argument follows default argument 非默认参数跟在默认参数后面
    def __init__(self, request, data_count, query_params ,per_pagination=11, per_page=10):
        self.path = request.path_info
        self.page = int(request.GET.get('page', 1))
        self.per_pagination = per_pagination
        self.half = per_pagination // 2
        self.per_page = per_page
        self.data_count = data_count
        self.query_params = query_params
        self.query_params._mutable = True  # _mutable改为True就可以修改字典

    def pre(self):
        return (self.page - 1) * self.per_page

    def next(self):
        return self.page * self.per_page

    @property  # 加入python 内置装饰器,使得方法调用转换为属性调用
    def get_html(self):
        num_page, remainder = divmod(self.data_count, self.per_page)
        if remainder:
            num_page += 1

        pagination = [str(i) for i in range(1, num_page + 1)]  # 页码数据列表

        if self.page > 0 and self.page < self.half + 1:
            start = 1
            end = self.per_pagination
        elif self.page > num_page - self.half:
            start = num_page - self.per_pagination + 1
            end = num_page
        else:
            start = self.page - self.half
            end = self.page + self.half

        #     # 存放li标签的列表
        html_list = []
        # 首页
        self.query_params['page'] = 1
        first_li = '<li><a href="{0}?{1}">首页</a></li>'.format(self.path, self.query_params.urlencode())
        html_list.append(first_li)
        # 上一页
        if self.page == 1:
            prev_li = '<li class="disabled"><a><<</a></li>'
        else:
            self.query_params['page'] = self.page - 1
            prev_li = '<li><a href="{0}?{1}"><<</a></li>'.format(self.path, self.query_params.urlencode())
        html_list.append(prev_li)
        # 中间显示页码数
        for p in pagination[start - 1:end]:
            self.query_params['page'] = p
            page_li = '<li><a href="{0}?{1}">{2}</a></li>'.format(self.path, self.query_params.urlencode(), p)
            html_list.append(page_li)
        # 下一页
        if self.page == num_page:
            next_li = '<li class="disabled"><a>>></a></li>'
        else:
            self.query_params['page'] = self.page + 1
            next_li = '<li><a href="{0}?{1}">>></a></li>'.format(self.path, self.query_params.urlencode())
        html_list.append(next_li)
        # 尾页
        self.query_params['page'] = num_page
        last_li = '<li><a href="{0}?{1}">尾页</a></li>'.format(self.path, self.query_params.urlencode())
        html_list.append(last_li)

        html_str = mark_safe(''.join(html_list))

        return html_str

六、模糊搜索功能与分页结合实现

翻页时,保留搜索条件;通过for循环逐一搜索添加的字段,条件为并列关系

from django.db.models import Q

class Show_Customer(View):

        def get(self, request, ret=None):
        # print(request.GET)
        q = self.get_search_contion(['qq', 'name', 'qq_name', 'last_consult_date'])
        if request.path_info == "/crm/":
            all_customer = models.Customer.objects.filter(q, consultant__isnull=True)
        else:
            if request.path_info == "/crm/all_customer/":  # 所有客户
                all_customer = models.Customer.objects.filter(q)
            else:
                all_customer = models.Customer.objects.filter(q, consultant=request.user)  # 我的客户
        query_params = copy.deepcopy(request.GET)  # 深度copy会获取原来的对象再生成一个,不会影响原来的结果
        # print(query_params.urlencode()) #<QueryDict: {'query': ['Laughing'], 'page': ['2']}> 会将QueryDict字典转化为url拼接的方式 query=Laughing&page=2
        len_obj = len(all_customer)
        page_obj = pagination.Page(request, len_obj, query_params)
        pre = page_obj.pre()
        next = page_obj.next()
        html_str = page_obj.get_html  # 使用了装饰器

        return render(request, 'crm/customer.html',
                      context={"all_customer": all_customer[pre:next], "html_str": html_str, "len_obj": len_obj,
                               "query_params": query_params, "ret": ret})
                
    def get_search_contion(self, query_list):

        query = self.request.GET.get('query', '')

        q = Q()
        q.connector = 'OR'
        for i in query_list:
            q.children.append(Q(('{}__contains'.format(i), query)))

        return q

七、保留路径

下次点击操作时,将前一次的路径通过?next=params参数来进行传递,所以下一次点击的结果会包含上一次路径的信息,当点击提交按钮时,会返回至next路径下的页面

    def add_record(self):
        path = self.request.get_full_path()  # 获得所有页面包括?之后的查询条件
        # url = self.request.path_info        #获得基地址页面
        # print(path, url)
        qd = QueryDict()
        qd._mutable = True
        qd['next'] = path
        # next=%2Fcrm%2Fcustomer_list%2F%3Fquery%3Dalex%26page%3D2
        query_params = qd.urlencode()

        # add_btn = '<a href="{}?next={}" class="btn btn-primary btn-sm">添加</a>'.format(reverse('add_customer'), url)
        add_btn = '<a href="{}?{}" class="btn btn-primary btn-sm">添加</a>'.format(reverse('add_customer'), query_params)

        return mark_safe(add_btn), query_params

八、批量操作

html代码

form action="" method="post" class="form-inline">
    {% csrf_token %}
    <select name="action" class="form-control" style="margin: 5px 0">
        <option value="">请选择</option>
        <option value="multi_delte">删除</option>
        {% if request.path_info == '/crm/' %}
        <option value="multi_apply">放入私户</option>
        {% elif request.path_info == '/crm/my_customer/' %}
        <option value="multi_pub">放入公户</option>
        {% else %}
        <option value="multi_apply">放入私户</option>
        <option value="multi_pub">放入公户</option>
        {% endif %}
        {#
        <option value=""></option>
        #}
    </select>
    <button class="btn btn-success btn-sm">提交</button>
    </form>

hasattr(object, name)是否存在属性name
getattr(object, name[, default]) 如果存在则返回属性值,如果不存在分为两种情况,一种是没有default参数时,会直接报错;给定了default参数,若对象本身没有name属性,则会返回给定的default值,如果给定的属性name是对象的方法,则返回的是函数对象,需要调用函数对象来获得函数的返回值;调用的话就是函数对象后面加括号,如func之于func();
setattr(object, name, value)给object对象的name属性赋值value
当多人同时申请同一个客户的时候,需要增加事务功能,才能保证不会出现争抢资源出现

    def post(self, request):
        # print(request.POST)
        action = request.POST.get('action')
        query = request.POST.get('query')
        if action:
            print(action)
            ret = getattr(self, action)(request)
            print(ret)
            return self.get(request, ret)
        else:
            return HttpResponse("非法操作")

    def multi_apply(self, request):
        id_list = request.POST.getlist('id')
        # obj = models.Customer.objects.filter(id__in=id_list).update(consultant=request.user)
        # print(settings.MAX_CUSTOMER_NUMBERS)
        if request.user.customers.count() + len(id_list) <= settings.MAX_CUSTOMER_NUMBERS:  # 控制添加客户数量
            with transaction.atomic():  # 添加事务
                obj = models.Customer.objects.filter(id__in=id_list, consultant__isnull=True).select_for_update()  # 加锁,
                if len(id_list) == len(obj):  # 判断,当第一次数量和原本数据量相等时,同意请求
                    time.sleep(1)  # 先申请的的人等待,后申请的人等之前的人
                    obj.update(consultant=request.user)
                    return "客户添加私有成功"
                else:
                    return "客户添加失败"
        else:
            return "超过客户添加上限:5"

    def multi_pub(self, request):
        id_list = request.POST.getlist('id')
        obj = models.Customer.objects.filter(id__in=id_list).update(consultant=None)
        return "客户添加公共成功"

    def multi_delte(self, request):
        id_list = request.POST.getlist('id')
        obj = models.Customer.objects.filter(id__in=id_list).delete()
        return "删除成功"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值