Django自定义条码扫码校验:如何判断序列号不超GRN对应产品数量
核心问题根因
原代码存在三个逻辑漏洞,导致无法正确读取入库数量完成校验:
Grn.objects.filter()返回的是Django查询集(QuerySet),不是单个模型实例,无法直接访问关联表的字段Grn模型的items是多对多关联字段,访问后返回的是关联的GrnItems查询集,不是单个明细行对象,直接通过items.item_quantity读取会触发字段不存在的报错- 缺少边界值防护:未处理GRN不存在、GRN下无对应商品的场景,也未对解析出的序列号做范围合法性判断
正确实现逻辑
不需要先查询Grn对象,直接从GrnItems明细模型出发,利用Django多对多的反向关联能力,一次查询同时匹配GRN ID、商品ID两个条件,取出对应明细行的入库数量完成校验,比先查GRN再查明细的写法减少一次数据库查询。
修正后的完整处理代码:
c = request.GET['code'].strip() # 前置校验条码基础格式,避免索引解析错误 if len(c) != 16 or not c.startswith("YNT"): return HttpResponse("无效条码格式") try: grn_id = int(c[3:7]) product_id = int(c[7:11]) serial_no = int(c[11:]) except ValueError: return HttpResponse("条码包含非法数字内容") # 核心修正:直接查询匹配的GrnItems明细行 # 因Grn模型未自定义related_name,GrnItems到Grn的反向访问器为默认值grn_set matched_item = GrnItems.objects.filter( grn_set__pk=grn_id, item__pk=product_id ).first() # 处理匹配不到明细的异常场景 if not matched_item: return HttpResponse("条码对应的GRN或商品不存在") total_stock = matched_item.item_quantity # 序列号范围为1~总入库量(对应生成规则中j+1从1开始计数) if 1 <= serial_no <= total_stock: res = Product.objects.get(pk=product_id).description # 后续扫码业务逻辑 else: return HttpResponse(f"序列号非法,该商品在本GRN下总入库{total_stock}件")
补充说明
- 如果觉得反向关联名
grn_set可读性差,可以在Grn模型的items字段定义中添加related_name="grn"参数,调整后GrnItems到Grn的反向访问器名会变为grn,查询时可以直接写grn__pk=grn_id,代码可读性更好。 - 当前模型的多对多关联设计存在业务逻辑隐患:正常入库单的明细行是入库单的独有从属内容,同一明细行不应被多个入库单复用。建议将关联关系调整为一对多:在
GrnItems中新增grn = models.ForeignKey(Grn, on_delete=models.CASCADE)字段,删除Grn模型中的items多对多字段,调整后上述查询逻辑完全兼容,且从结构上避免了明细被多单复用的逻辑漏洞。 - 若业务中存在同一GRN下同一商品对应多条入库明细的场景,当前仅携带商品ID的条码规则会出现重复,需要在条码中补充
GrnItems的主键字段做唯一区分。
内容的提问来源于stack exchange,提问作者Rahul Sharma
相关产品推荐
相关产品推荐

