Django批量保存JSON对象时更新datetime字段的实现方案
Django批量保存JSON对象时更新datetime字段的实现方案
我明白你的需求啦——批量提交JSON数组到Django API,遇到已存在的记录(根据ip、port、type的唯一组合)就更新它的时间字段,不存在就新建。咱们一步步来解决这个问题:
第一步:调整模型的时间字段(可选但更合理)
你当前的created字段用了auto_now_add=True,这个只会在记录第一次创建时自动赋值,后续更新不会改变它。如果你的需求是“更新时间字段”,更合理的做法是新增一个updated字段专门用于记录更新时间,保留created作为创建时间:
class CheckPort(models.Model): created = models.DateTimeField(auto_now_add=True) # 仅记录创建时间,永不修改 updated = models.DateTimeField(auto_now=True) # 每次保存自动更新为当前时间 ip = models.CharField(max_length=15, blank=False) port = models.PositiveIntegerField(default=0) type = models.CharField(max_length=5, default='tcp') class Meta: ordering = ['created'] unique_together = ('ip', 'port', 'type')
如果你就是想直接更新created字段,那把它改成auto_now=True即可,这样每次保存都会覆盖为当前时间。
第二步:修改API视图,实现批量处理逻辑
默认的Serializer批量保存只会创建新记录,不会处理已存在的情况,咱们需要自定义这部分逻辑。这里有两种方案,你可以根据数据量选择:
方案一:逐行处理(适合小批量数据)
这种方式逻辑直观,容易调试,适合数据量不大的场景:
from django.utils import timezone from rest_framework import status, Response from rest_framework.decorators import api_view from .models import CheckPort from .serializers import OfferSerializer @api_view(['GET', 'POST']) def port_list(request): if request.method == 'GET': offers = CheckPort.objects.all() serializer = OfferSerializer(offers, many=True) return Response(serializer.data) elif request.method == 'POST': is_batch = isinstance(request.data, list) serializer = OfferSerializer(data=request.data, many=is_batch) if serializer.is_valid(): # 处理批量提交 if is_batch: processed_instances = [] for item in serializer.validated_data: # 查找是否存在该记录,不存在则创建 instance, created = CheckPort.objects.get_or_create( ip=item['ip'], port=item['port'], type=item['type'], defaults=item ) # 如果已存在,更新时间字段 if not created: instance.updated = timezone.now() # 如果你要更新created就改成instance.created instance.save() processed_instances.append(instance) # 序列化处理后的结果返回 response_serializer = OfferSerializer(processed_instances, many=True) return Response(response_serializer.data, status=status.HTTP_201_CREATED) # 处理单个对象提交 else: instance, created = CheckPort.objects.get_or_create( ip=serializer.validated_data['ip'], port=serializer.validated_data['port'], type=serializer.validated_data['type'], defaults=serializer.validated_data ) if not created: instance.updated = timezone.now() instance.save() response_serializer = OfferSerializer(instance) # 已存在的记录返回200,新建返回201,更符合REST规范 return Response( response_serializer.data, status=status.HTTP_201_CREATED if created else status.HTTP_200_OK ) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
方案二:批量操作(适合大批量数据)
如果你的数据量很大,逐行处理效率太低,可以用Django的bulk_create配合update_conflicts参数(Django 2.2及以上版本支持),这会生成一条SQL语句完成批量创建和更新,效率高很多:
from django.db.models import Q from rest_framework import status, Response from rest_framework.decorators import api_view from .models import CheckPort from .serializers import OfferSerializer @api_view(['GET', 'POST']) def port_list(request): if request.method == 'GET': offers = CheckPort.objects.all() serializer = OfferSerializer(offers, many=True) return Response(serializer.data) elif request.method == 'POST': is_batch = isinstance(request.data, list) serializer = OfferSerializer(data=request.data, many=is_batch) if serializer.is_valid(): if is_batch: # 批量创建对象实例(不会立即保存到数据库) objs = [CheckPort(**item) for item in serializer.validated_data] # 批量保存,存在冲突时更新指定字段 CheckPort.objects.bulk_create( objs, update_conflicts=True, unique_fields=['ip', 'port', 'type'], # 唯一约束字段 update_fields=['updated'] # 需要更新的字段,改成['created']如果你要更新它 ) # 查询所有处理过的记录返回 query = Q() for item in serializer.validated_data: query |= Q(ip=item['ip'], port=item['port'], type=item['type']) processed_instances = CheckPort.objects.filter(query) response_serializer = OfferSerializer(processed_instances, many=True) return Response(response_serializer.data, status=status.HTTP_201_CREATED) # 单个对象处理逻辑和方案一一致 else: instance, created = CheckPort.objects.get_or_create( ip=serializer.validated_data['ip'], port=serializer.validated_data['port'], type=serializer.validated_data['type'], defaults=serializer.validated_data ) if not created: instance.updated = timezone.now() instance.save() response_serializer = OfferSerializer(instance) return Response( response_serializer.data, status=status.HTTP_201_CREATED if created else status.HTTP_200_OK ) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
第三步:更新序列化器
确保你的OfferSerializer包含了新增的updated字段(如果修改了模型的话):
from rest_framework import serializers from .models import CheckPort class OfferSerializer(serializers.ModelSerializer): class Meta: model = CheckPort fields = ['id', 'created', 'updated', 'ip', 'port', 'type'] # 包含所有需要返回的字段
这样就搞定啦!测试一下批量提交JSON数组,已存在的记录会自动更新时间字段,新记录会被创建。
备注:内容来源于stack exchange,提问作者Gosha Efimenko
相关产品推荐
相关产品推荐

