如何使用条件案例进行Django更新?

时间:2013-06-17 19:39:09

标签: django

我想使用Django将字段更新为不同的值,具体取决于其当前值,但我没有想到如何在不执行2个单独的更新语句的情况下执行此操作。

以下是我想做的一个例子:

now = timezone.now()
data = MyData.objects.get(pk=dataID)
if data.targetTime < now:
    data.targetTime = now + timedelta(days=XX)
else:
    data.targetTime = data.targetTime + timedelta(days=XX)
data.save()

现在,我想使用update()语句来避免覆盖我的数据上的其他字段,但我不知道如何在单个update()中执行此操作。我尝试了这样的代码,但是第二次更新没有使用最新时间(我最终得到了一个等于当前时间的字段):

# Update the time to the current time
now = timezone.now()
MyData.objects.filter(pk=dataID).filter(targetTime__lt=now).update(targetTime=now)
# Then add the additional time
MyData.objects.filter(pk=dataID).update(targetTime=F('targetTime') + timedelta(days=XX))

有没有办法可以将其减少为单个update()语句?类似于SQL CASE语句的东西?

5 个答案:

答案 0 :(得分:3)

如果我理解正确,您需要从现在到数据库中的值之间的最长时间。

如果是这样,你可以使用max函数在一行中完成:

from django.db.models import F
MyData.objects.filter(pk=dataID).update(targetTime=max(F('targetTime'),timezone.now()) + timedelta(days=XX))

答案 1 :(得分:3)

您需要使用conditional expressions,就像这样

from django.db.models import Case, When, F

object = MyData.objects.get(pk=dataID)
now = timezone.now()
object.targetTime = Case(
    When(targetTime__lt=now, then=now + timedelta(days=XX)),
    default=F('targetTime') + timedelta(days=XX)
)
object.save(update_fields=['targetTime'])

对于调试,请尝试在save之后运行此命令以查看刚刚运行的SQL查询:

import pprint
from django.db import connection
pprint.pprint(["queries", connection.queries])

我用整数测试了它,它在Django 1.8中工作,我还没有尝试过日期,所以可能需要一些调整。

答案 2 :(得分:2)

使用queryset.update(...)(请参阅https://docs.djangoproject.com/en/dev/ref/models/instances/#specifying-which-fields-to-save),而不是使用obj.save(update_fields=['field_one', 'field_two']),而不会覆盖现有字段。

如果没有首先选择查询(get),就不可能这样做,因为你根据条件做了两件不同的事情(也就是说,你不能将这种逻辑传递给数据库Django - 使用F)可以实现的限制是有限制的,但至少这会让你获得一次插入/更新。

答案 3 :(得分:1)

Django 1.9增加了最伟大和最少database functions。这是对Benjamin Toueg的答案的改编:

from django.db.models import F
from django.db.models.functions import Greatest


MyData.objects.filter(pk=dataID).update(
    targetTime=Greatest(F('targetTime'), timezone.now()) + timedelta(days=XX)
)

答案 4 :(得分:0)

我已经弄清楚如何使用原始SQL语句:

cursor = connection.cursor()
cursor.execute("UPDATE `mydatabase_name` SET `targetTime` = CASE WHEN `targetTime` < %s THEN %s ELSE (`targetTime` + %s) END WHERE `dataID` = %s", [timezone.now(), timezone.now() + timedelta(days=XX), timedelta(days=XX), dataID])
transaction.commit_unless_managed()

我现在正在使用它,它似乎正在实现我想要的东西。