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

使用Django ORM filter更新时意外修改start字段问题求助

问题:Django批量更新时意外修改了start字段

我用Django Rest Framework写了一个简单的api_view,意图通过患者ID过滤查询集,只筛选start日期大于今日的预约记录,然后将这些记录的is_cancelled字段设为True。但执行更新操作时,start字段会被自动设置为视图运行时的当前日期时间,而我只想要更新is_cancelled字段。

视图代码如下:

@api_view(['POST'])
def BookingCancelAppointmentsView(request):
    if request.method == 'POST':
        patient_id = request.data
        today = date.today()
        bookings = Booking.objects.filter(patient=patient_id, start__gte=today).update(is_cancelled=True)
    return Response({'message': 'Appointments cancelled'})

Booking模型参考:

class Booking(models.Model):
    title = models.CharField(max_length=500, blank=True, null=True)
    patient = models.ForeignKey(Patient, on_delete=models.PROTECT, blank=True, null=True, related_name='bookings')
    start = models.DateTimeField(auto_now=False, auto_now_add=False)
    end = models.DateTimeField(auto_now=False, auto_now_add=False, blank=True, null=True)
    is_all_day = models.BooleanField(default=False)
    is_personal = models.BooleanField(default=False)
    practitioner = models.ForeignKey(Practitioner, on_delete=models.SET_NULL, blank=True, null=True, related_name='booking_practitioners')

    def __str__(self):
        if not self.patient:
            return self.practitioner.practitioner.get_full_name()

        return str(self.patient.get_full_name())

    class Meta:
        unique_together = ('patient', 'start', 'practitioner')

软件版本:

  • Python版本:3.10.5
  • Django版本:3.2.15
  • Django Rest Framework版本:3.13.1

原因与解决方案

原因

问题根源在unique_together约束:你的Booking模型在Meta类中定义了unique_together = ('patient', 'start', 'practitioner'),要求这三个字段的组合必须唯一。

Django 3.2版本在处理带有unique_together约束的批量更新时存在一个bug:如果过滤条件用到了约束中的字段(这里是start__gte=today),框架会错误地将过滤条件中的值(today)赋值给该字段,导致start被意外修改。这个问题在后续版本中已被修复。

解决方案

方法一:升级Django版本

将Django升级到3.2.16及以上的稳定版本,该版本修复了批量更新时因unique_together约束导致的字段意外赋值问题。

方法二:修改过滤条件,明确匹配日期部分

把start__gte=today替换为start__date__gte=today,明确指定匹配start字段的日期部分,而非直接用日期对象匹配DateTimeField:

@api_view(['POST'])
def BookingCancelAppointmentsView(request):
    if request.method == 'POST':
        # 注意:建议用get方法获取具体字段,避免直接使用request.data
        patient_id = request.data.get('patient_id')
        today = date.today()
        bookings = Booking.objects.filter(patient=patient_id, start__date__gte=today).update(is_cancelled=True)
    return Response({'message': 'Appointments cancelled'})

修改后,Django只会用today过滤记录,不会将其赋值给start字段,同时保留unique_together约束的有效性。

另外补充:原代码中patient_id = request.data存在隐患,前端传递的JSON数据通常需要通过request.data.get('patient_id')获取具体ID值,避免因类型不匹配引发错误。

内容的提问来源于stack exchange,提问作者jAC

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 02:01:15