You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 20:08:04