DRF设备序列号掩码验证失效问题排查与修复咨询
问题分析与修复方案
你的验证逻辑失效主要是因为模型层、序列化器层的代码存在多处错误,以下是具体修改点:
一、models.py 修正
错误点:
mask = self.type错误获取了EquipmentType对象,而非其mask字段- 正则表达式中
'd'未转义,实际匹配的是字符d而非数字 re.match未限制完整匹配,导致部分符合开头的字符串也能通过- ValidationError的字段名
serial_number与模型字段sn不匹配 - 模型save方法未主动调用clean,导致模型层验证不触发
修改后的代码:
import re from django.core.exceptions import ValidationError from django.db import models class EquipmentType(models.Model): name = models.CharField(max_length=255) mask = models.CharField(max_length=100) class Equipment(models.Model): type = models.ForeignKey('EquipmentType', on_delete=models.CASCADE) sn = models.CharField(max_length=255, unique=True) note = models.CharField(max_length=255) def clean(self): super().clean() # 调用父类clean方法 mask = self.type.mask # 正确获取掩码字段 regex = '^' # 匹配字符串开头 for char in mask: if char == 'N': regex += r'\d' # 转义匹配数字 elif char == 'A': regex += '[A-Z]' elif char == 'a': regex += '[a-z]' elif char == 'X': regex += '[A-Z0-9]' elif char == 'Z': regex += '[-_@]' else: regex += re.escape(char) # 转义掩码中的特殊字符 regex += '$' # 匹配字符串结尾 if not re.fullmatch(regex, self.sn): # fullmatch确保完整匹配 raise ValidationError({'sn': '序列号不符合当前设备类型的掩码规则'}) def save(self, *args, **kwargs): self.clean() # 保存前强制调用clean验证 super().save(*args, **kwargs) class Meta: unique_together = ('sn', 'type')
二、serializers.py 修正
错误点:
- 验证方法名错误:应为
validate_sn而非validate_serial_number,DRF要求字段验证方法命名为validate_<字段名> - 创建新对象时
self.instance为None,直接访问会报错 - 循环遍历
value(sn字符串)的每个字符进行验证,逻辑错误 - 正则表达式同样存在未转义、未完整匹配的问题
修改后的代码:
import re from rest_framework import serializers from .models import Equipment, EquipmentType class EquipmentSerializer(serializers.ModelSerializer): class Meta: model = Equipment fields = '__all__' def validate_sn(self, value): # 获取当前设备对应的类型:创建时从initial_data取,更新时从instance取 if self.instance: equipment_type = self.instance.type else: type_id = self.initial_data.get('type') if not type_id: raise serializers.ValidationError('必须指定设备类型') try: equipment_type = EquipmentType.objects.get(id=type_id) except EquipmentType.DoesNotExist: raise serializers.ValidationError('指定的设备类型不存在') mask = equipment_type.mask regex = '^' for char in mask: if char == 'N': regex += r'\d' elif char == 'A': regex += '[A-Z]' elif char == 'a': regex += '[a-z]' elif char == 'X': regex += '[A-Z0-9]' elif char == 'Z': regex += '[-_@]' else: regex += re.escape(char) regex += '$' if not re.fullmatch(regex, value): raise serializers.ValidationError(f'序列号不符合掩码规则:{mask}') return value class EquipmentTypeSerializer(serializers.ModelSerializer): class Meta: model = EquipmentType fields = '__all__'
三、views.py 修正
错误点:
EquipmentTypeAPIView的get方法中,错误使用Equipment模型获取对象,应改为EquipmentType
修改后的代码片段:
class EquipmentTypeAPIView(APIView): def get(self, request, *args, **kwargs): pk = kwargs.get("pk", None) if not pk: w = EquipmentType.objects.all() return Response({'posts': EquipmentTypeSerializer(w, many=True).data}) # 修正这里的模型为EquipmentType mymodel = get_object_or_404(EquipmentType, pk=pk) serializer = EquipmentTypeSerializer(mymodel) return Response(serializer.data)
额外说明
- 模型层的clean+save调用确保了即使绕过序列化器直接操作数据库,验证也会生效
- 序列化器层的验证针对API请求做了更友好的错误提示,同时处理了创建和更新两种场景
- 使用
re.fullmatch替代re.match,确保整个序列号完全符合掩码长度和规则
内容的提问来源于stack exchange,提问作者Александр Филипов
相关产品推荐
相关产品推荐

