我想使用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语句的东西?
答案 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()
我现在正在使用它,它似乎正在实现我想要的东西。