当前位置: 首页 > news >正文

Django项目之订单管理part5

一.前言

我们前面已经做了级别管理和客户管理,讲的时间比较长,因为补充的知识点比较多,而今天我们尽可能说完剩下的全部功能,比如说价格策略啥的,这个表单特别简单,我们也没啥可以补充的知识点的话,就可以直接展示截图了。

二.价格策略

2.1 价格策略基础页面

这个和前面基本都一样,我们就不过多赘述了,我们提前创建一个form

class PolicyModelForm(BootStrapForm,forms.ModelForm):
    class Meta:
        model=models.PricePolicy
        fields='__all__'
def policy_list(request):
    queryset = models.PricePolicy.objects.all().order_by('count')
    pager = Pagination(request, queryset)
    return render(request, 'policy/policy_list.html', {'pager': pager})

html和之前的都一样,我就不多说了。

2.2 新建价格策略 

def policy_add(request):
    if request.method == "GET":
        form = PolicyModelForm()
        return render(request, 'policy/policy_form.html', {'form': form})
    form = PolicyModelForm(data=request.POST)
    if not form.is_valid():
        return render(request, 'policy/policy_form.html', {'form': form})
    # 2.添加到数据库
    form.save()
    return redirect(reverse('policy_list'))

2.3 编辑价格策略 

def policy_edit(request, pk):
    level_object = models.PricePolicy.objects.filter(id=pk).first()
    if request.method == "GET":
        form = PolicyModelForm(instance=level_object)
        return render(request, 'policy/policy_form.html', {'form': form})

    # 获取数据+校验
    form = PolicyModelForm(data=request.POST, instance=level_object)
    if not form.is_valid():
        return render(request, 'policy/policy_form.html', {'form': form})
    form.save()
    return redirect(reverse('policy_list'))

2.4 删除价格策略 

def policy_delete(request, pk):
    exists = models.PricePolicy.objects.filter(id=pk).exists()
    if not exists:
        res = BaseResponse(status=False, detail='请选择要删除的数据')
        return JsonResponse(res.dict)

    res = BaseResponse(status=True)
    models.PricePolicy.objects.filter(id=pk).delete()
    return JsonResponse(res.dict)

因为这个也是要有弹窗,所以我们就把公共的部分拿出来,要用的时候就导入。

我们就这样,谁要用就导入就可以了,这里给出代码

html:

<div class="modal fade" id="deleteModel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
        <div class="alert alert-danger alert-dismissible fade in" role="alert">
            <h4>是否确定删除</h4>
            <p>点击确定后删除 (删除后将无法复原) !</p>
            <p>
                <button type="button" class="btn btn-danger" id="btnConfirmDelete">确 定</button>
                <button type="button" class="btn btn-default" id="btnCancelDelete">取 消</button>
                <span style="color: red" id="deleteError"></span>
            </p>
        </div>
    </div>
</div>

js:

$(function () {
    bindDeleteEvent()
    bindConfirmEvent()
})

function bindDeleteEvent() {

    $('.btn-delete').click(function () {
        $('#deleteError').empty()
        $('#deleteModel').modal('show')
        DELETE_ID = $(this).attr('cid')
    })

    $('#btnCancelDelete').click(function () {
        $('#deleteModel').modal('hide')
    })
}

function bindConfirmEvent() {

    $('#btnConfirmDelete').click(function () {
        //找到了要删除的id,通过ajax请求删除
        $.ajax({
            url: DELETE_URL+DELETE_ID+'/',
            method: 'POST',
            dataType: 'JSON',
            success: function (res) {
                if (res.status) {
                    //方式一:页面刷新
                    //location.reload()
                    //上市二:当前数据在页面上删除
                    $(`tr[rid='${DELETE_ID}']`).remove()
                    $('#deleteModel').modal('hide')
                } else {
                    $('#deleteError').text(res.detail)
                }
            }
        })

    })
}

这样就成功写好了价格策略,是不是发现很简单。

三.优化权限页面 

我们可以测试一下,假如我们给添加这个全选给取消掉,我们是不是点击之后添加之后会进入无权访问页面,但是我们不想这样,我们希望无权限的人连按钮都看不到,这需要怎么做呢?我们首先想到的是利用if判断,但是在html里面很难实现这种逻辑,所以我们可以借助自定义模板方法来实现。

自定义模板方法回归:(不记得的可以去看前面的知识点)

在模板中自定义方法:

  • filter

    "xxxx"|upper
  • sample_tag

    {% xxxx x1 x2 x3 %}
    def xxx():
        return ""
  • inclusion_tag

    def xxx():
        return {'v1':xx,'v2':xx}
    <h1>{{v1}}</h1>

这里我们选择sample_tag来实现,这是因为我们在里面判断之后直线返回字符串,如果没有的话就是一个空字符串,没有必要用inclusion_tag来创建模板。

我们直接这么写,就好了,只要我们把权限都关掉,就不会出现这个按钮啦!

四.页面跳转问题

4.1 大部分页面跳转 

我们可以创建多个客户数据,然后我们测试一下就知道了,当我们不是在第一页删除或者是编辑的时候,我们最后都会跳转到第一页,这个是因为我们最后跳转的时候都是写死的,没有写上原来页面上的参数参数,所以我们需要再每次点请求的时候把这个参数携带过去,但是如果我们写上一个参数,这个很容易会和本来要的数据产生冲突,我们比较难处理,所以我们可以把那些参数当成是一个字符串然后用一个filter参数接收,类似于filter=urlencode(name=123&page=15)这样传过去再对后面的进行处理,我们拿编辑举例

我们先来说一下我们请求,我们直接先是把上一个请求的params取出来,然后再利用QueryDict把它变成是一个_filter=url编码后的字符串,然后直接传给标签里面,这样下次点击的时候就是这样一个标签了,数据也都携带过去了。

返回的时候我们就是先判断有没有_filter如果没有就直接重定向,如果有的话就直接加上这个filter后的的字符串,加到反向生成之后,这样就可以了。

然后我们可以在除了删除以外的都加上这个并且返回,因为我们删除用的是ajax,并且除了客户需要跳转输入验证码,其他的都是删除页面元素,并不涉及到重定向,而除了客户我们需要单独写页面跳转。

4.2 客户管理删除页面跳转

这个就非常简单了,我们返回末尾加上上面那个

这里再判断一下就非常简单了,主要还是要知道处理的思路,用_filter包裹一下就非常简单了

五.充值 

我们接下来要说的就是充值,我们希望我们可以实现给用户充值或者是扣款,这次我们就不去页面跳转,而是一个弹出框,选择充值还是扣款,并且生成一条交易记录展示到页面,那我们第一步就是要先去写页面了。

5.1 交易记录和对话框

  

我们这个还是和之前一样展示页面,点击添加就会出现一个弹出框,然后呢,弹出框里面有一个form表单,我们点击就可以通过ajax提交,这里我们,就是写了个基础的页面,这里需要注意的是我们想要展示到页面上的是charge_type是中文而不是那个数字,就需要

get_charge_type_display

但是我们这里只想写给用户充值扣款的功能,所以我们需要改变charge_type字段,这里可以选择重写或者是更改__init__中charge_tpye的choices字段,但是这两个有点区别,上面是静态的,如果更新了就只能从重启django项目,而下面的每次更新都会从数据库重新读取,但是这里我们用两种都是可以的,但是如果下面是选择充值的管理员,就不能用上面的了,因为我们项目一旦部署,而我们要是添加了管理员,就会得不到那个管理员。

5.2 充值扣款功能 

做完上面的页面之后,我们就需要点击提交,像后端发送ajax请求,更新数据了。

我们首先肯定是要加上一个错误提示的位置的

再写上一个click事件,发送ajax请求

我们后端就是对数据进行更新,然后进行操作,但是这里需要注意的是我圈出来的位置,收先第一个就是 transaction.atomic(),这个我们是 from django.db import transaction调用的,我们这个是为什么要加上这个呢?这个是因为这个涉及了多个数据库,并且和钱有关系,我们这们做是要让两个数据库操作变成一个原子形操作,两个都成功了才能成功,我们可以想一下,第一个我们这边成功给他充值,但是下面生成订单的时候报错了,没有成功生成,那么就会出现对不上账,这就很危险,select_for_update() 而查询后加上这个,是相当于加了锁,配合transaction同时使用,防止两个管理员同时对数据库操作而导致一个没操作上,尤其是对于钱,一定要把条件弄得严格,这样就是一个很健壮的程序了。

大功告成 

5.3 页面充值优化

我们看看刚才这个页面,是不是特别的不直观,我们前面是不是设置了一下一个mapping,里面有个样式,现在就是我们使用的时候了,此时此刻是不是得判断,取出model里的值,那我们是不是就要借助 filter 自定义一个函数

我们先写,直接去models里面取,这里因为不用数据库,就不用加objects 

而且我们想要把没有内容的地方,比如订单号和备注不要展示成none,所以我们就加上个if判断 

效果就是现在这样了 

六.我的订单管理 (客户)

我们现在要做的订单管理,是基于用户的,我们要实现的就是用户能够看得到自己的订单,我们还是老规矩,不管了,先加钠。

6.1 我的订单基础页面

这里我们还是直接写,但是大家记住,权限都是加到客户里的而不是加到管理员,大家这个需要注意一下

这样就ok了

6.2 创建订单基础页面

因为创建订单的话非常的繁琐,所以这里先给出一个基础页面 

我们还是创建一个form,大家肯定可以发现form大多数都是可以多次利用的,大家别学我,非要把form放在每个里面,其实这个没必要拆开,这样反而增加了工作量

  

我们这里只写了基础的校验啥的,后续还有一些列操作还没写,所以单开一个标题 

这样就是创建好了

6.3 创建订单流程 

这里先给大家说一个小的知识点,就是关于更新,这里要引入一个知识点就是django中的F关键字

6.3.1 F关键字

前面我们说了一个Q关键字,是在复杂条件查询的时候我们可以用的到,我们先说F关键字的应用场景,就是当我们要给原来数据库中比如说是cout自增1的时候就用得到

关于更新:

对数据直接更新

models.Customer.objects.filter(id=22).update(name='往日情怀酿作酒',count=1999)

想在原有值的基础上更新

cus_object= models.Customer.objects.filter(id=22).first()

cus_object.name ='往日情怀酿作酒'
cus_object.count=cus_object.count + 10
cus_object.save()

大家肯定觉得很繁琐,但是有F关键字就不一样了

from django.db.models import F

models.Customer.objects.filter(id=22).update(name='xxxx' ,count=F("count")+1000)

这样就能直接操作在原有基础上进行删除啦!

6.3.2 流程代码

def my_order_add(request):
    if request.method == "GET":
        form = MyOrderModelForm()
        return render(request, 'my_order/my_order_form.html', {'form': form})
    form = MyOrderModelForm(data=request.POST)
    if not form.is_valid():
        return render(request, 'my_order/my_order_form.html', {'form': form})

    # 1.获取url和count
    video_url = form.cleaned_data['url']
    count = form.cleaned_data['count']

    # 1.1 通过url获取原播放 为了防止一会加上锁影响性能
    status, old_view_count = get_old_view_count(video_url)
    if not status:
        form.add_error('url', '视频原来播放获取失败')
        return render(request, 'my_order/my_order_form.html', {'form': form})

    # 2.根据数量获取单价,计算出原价
    for idx in range(len(form.price_count_list) - 1, -1, -1):
        limit_count, unit_price = form.price_count_list[idx]
        if count >= limit_count:
            break
    total_price = count * unit_price

    # 3.获取当前客户所处的级别计算折扣后的价格
    try:
        with transaction.atomic():
            cus_object = models.Customer.objects.filter(id=request.userinfo.id).select_related(
                'level').select_for_update().first()
            real_price = total_price * cus_object.level.percent / 100

            # 4.判断账户余额是否不足
            if cus_object.balance < real_price:
                form.add_error('count',
                               '账户余额不足,本次消耗{:.2f}元,账户余额只有{}'.format(real_price, cus_object.balance))
                return render(request, 'my_order/my_order_form.html', {'form': form})

            # 5.创建订单
            # 5.1创建订单号
            while True:
                rand_number = random.randint(10000000, 99999999)
                ctime = datetime.datetime.now().strftime("%d%m%Y%H%M%S%f")
                oid = "{}{}".format(ctime, rand_number)
                exists = models.Order.objects.filter(oid=oid).exists()
                if not exists:
                    break

            # 5.2 创建订单
            form.instance.oid = oid
            form.instance.price = total_price
            form.instance.real_price = real_price
            form.instance.old_view_count = old_view_count
            form.instance.customer_id = request.userinfo.id
            form.save()

            # 6 客户账号扣款
            models.Customer.objects.filter(id=request.userinfo.id).update(balance=F('balance') - real_price)

            # 7 生成交易记录
            models.TransactionRecord.objects.create(
                charge_type=3,
                customer_id=request.userinfo.id,
                amount=real_price,
                order_oid=oid
            )

            # 8 写入redis队列
            conn = get_redis_connection('default')
            conn.lpush(settings.QUEUE_TASK_NAME, oid)
    except Exception as e:
        form.add_error('count', '创建订单失败')
        return render(request, 'my_order/my_order_form.html', {'form': form})
    return redirect(reverse('my_order_list'))

这个代码特别长,这里就不和大家讲解了,反正这样就能实现了一个创建订单的逻辑。

七.总结 

今天说的内容还是很多的,我们这一章还是没能讲完,不过后面也就一点内容了,基本功能都大差不差了,但是在订单这部分还是差一个撤单,但是想要加上这个内容还是很多,还要再讲一个message知识点,所以就留到下一期和worker一起讲了。

八.补充 

下一期将和大家开始讲的内容有点多,希望大家的关注加收藏,不懂得看我的名字和签名,一起交流学习

相关文章:

  • 前后端+数据库的项目实战:hbu迎新网-较复杂(下)javaweb
  • 运费服务模块通用需求分析
  • 算法刷题整理合集(七)·【算法赛】
  • Java面试黄金宝典12
  • STM32基础教程——PWM驱动LED呼吸灯
  • 【数理基础】【概率论与数理统计】概率论与数理统计本科课程总结、资料汇总、个人理解
  • 《AI大模型开发笔记》企业RAG技术实战(二)
  • AI比人脑更强,因为被植入思维模型【19】三脑理论思维模型
  • 我未来计划的Computer Use产品与对照组,即迷你版的AIGC中文脚本语言的区别
  • 本地部署Dify 添加Ollama模型DeepSeek
  • 2024年数维杯数学建模C题天然气水合物资源量评价解题全过程论文及程序
  • 全星研发管理 APQP 软件系统:以功能优势赋能企业研发
  • 死锁:当程序 “卡住“ 时,发生了什么?
  • UDP套接字编程(代码)
  • 聊聊langchain4j的MCP
  • TreeKEM 原理解析
  • ACF介绍及选用规则
  • CF1011(Div.2)A~D
  • 数组,指针 易混题解析(二)
  • Python---数据分析(Pandas七:二维数组DataFrame中元素的索引与访问,其他常用方法)
  • 国家统计局公布2024年城镇单位就业人员年平均工资情况
  • 会谈时间迟迟未定、核心议题存在分歧,俄乌“土耳其谈判”一波三折
  • 中国证券业协会修订发布《证券纠纷调解规则》
  • 中国至越南河内国际道路运输线路正式开通
  • 国务院关税税则委员会关于调整对原产于美国的进口商品加征关税措施的公告
  • 习近平举行仪式欢迎巴西总统卢拉访华