Django:在更新调用中对文本字段使用F表达式

Django:在更新调用中对文本字段使用F表达式,django,Django,在django视图中,我需要将字符串数据附加到数据库中现有文本列的末尾。例如,假设我有一个名为“ATable”的表,它有一个名为“aField”的字段。我希望能够在“aField”的末尾添加一个字符串,以一种无种族条件的方式。最初,我有: tableEntry = ATable.objects.get(id=100) tableEntry.aField += aStringVar tableEntry.save() 问题是,如果这是并发执行的,两者都可以获得相同的“tableEntry”,然后

在django视图中,我需要将字符串数据附加到数据库中现有文本列的末尾。例如,假设我有一个名为“ATable”的表,它有一个名为“aField”的字段。我希望能够在“aField”的末尾添加一个字符串,以一种无种族条件的方式。最初,我有:

tableEntry = ATable.objects.get(id=100)
tableEntry.aField += aStringVar
tableEntry.save()
问题是,如果这是并发执行的,两者都可以获得相同的“tableEntry”,然后它们各自独立更新,最后一个“保存”的将获胜,丢失另一个附加的数据

我仔细研究了一下,发现这一点,我希望它能起作用,使用F表达式:

ATable.objects.filter(id=100).update(aField=F('aField') + aStringVar)
这里的问题是,我得到一个SQL错误,说:

operator does not exist: text + unknown
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.

尝试更改为“str(aStringVar)”,即使它已经是一个字符串-没有运气。。我发现一些django bug报告抱怨类似的问题,但我没有看到修复或解决方法。是否有某种方法可以强制转换aStringVar,以便将其附加到F表达式的文本中?顺便说一句-也尝试了“str(F('aField'))+aStringVar”,但这将F表达式的结果转换为字符串(默认值:)。

似乎您无法做到这一点。但是,您试图做的事情可以使用


(看起来您正在使用postgres,因此,如果您希望在一个查询中执行此操作,并按照建议使用原始sql,
|
是您需要的连接运算符)

并且如果运行此操作,则它不是线程安全的。当更新正在运行时,其他一些进程可以在不知道数据库中的数据已更新的情况下更新模型

你也有一把锁,但别忘了,塞纳里奥:

  • Django:m=Model.objects.all()[10]
  • Django:m.field=field
  • Django:需要一段时间的进步(time.sleep(100))
  • DB:锁表
  • DB:更新字段
  • DD:解锁表
  • Django:这个缓慢的过程结束了
  • Django:m.save()

  • 现在,Django(Ghost write)中的模型实例撤消了字段更新。

    您可以使用Django的select_for_update()操作符实现此功能:

    大概是这样的:

    obj = ATable.objects.select_for_update().get(id=100)
    obj.aField = obj.aField + aStringVar
    obj.save()
    
    FACEBOOK_URI = 'graph.facebook.com'
    FACEBOOK_LARGE = '?type=large'
    # ...
    users = User.objects.filter(Q(avatar_uri__icontains=FACEBOOK_URI) & ~Q(avatar_uri__icontains=FACEBOOK_LARGE))
    users.update(avatar_uri=Concat('avatar_uri', Value(FACEBOOK_LARGE)))
    

    当您调用时,表行将被锁定。选择_for_update().get(),当您调用.save()时,锁定将被释放,允许您以原子方式执行操作。

    您可以通过一个简单的更改覆盖Django中的
    F
    对象:

    CF(F)级:
    加法=“||”
    
    然后用
    CF
    代替
    F
    。生成SQL时,它将放置“| |”而不是“+”。例如,查询:

    User.objects.filter(pk=100).update(email=CF('username')+'@gmail.com'))
    
    将生成SQL:

    更新“auth_user”设置“email”=“auth_user”。“username”| |“@gmail.com”
    其中“auth_user”。“id”=100
    
    您可以使用db功能

    from django.db.models import Value
    from django.db.models.functions import Concat
    
    ATable.objects.filter(id=100).update(some_field=Concat('some_field', Value('more string')))
    
    在我的例子中,我为facebook头像URI添加了一个后缀,如下所示:

    obj = ATable.objects.select_for_update().get(id=100)
    obj.aField = obj.aField + aStringVar
    obj.save()
    
    FACEBOOK_URI = 'graph.facebook.com'
    FACEBOOK_LARGE = '?type=large'
    # ...
    users = User.objects.filter(Q(avatar_uri__icontains=FACEBOOK_URI) & ~Q(avatar_uri__icontains=FACEBOOK_LARGE))
    users.update(avatar_uri=Concat('avatar_uri', Value(FACEBOOK_LARGE)))
    
    我得到的SQL如下(Django 1.9):


    结果是所有图像URI都从
    http://graph.facebook.com//picture
    http://graph.facebook.com//picture?type=large

    你能看到Django试图执行的sqlquery吗?我认为Daniel对另一个问题的解释可能是完全相同的。或者,你也可以通过写信来进行更新。我在网上搜索了很多次,但都没有找到答案,谢谢你给我指点。。我目前的解决方法是在django中使用带有游标的原始SQL,但它非常难看,并将我们与postgres联系在一起,这是我宁愿避免的。所以我做了原始的事情,这让我一直坚持到现在,但我使用的是多处理,并且有两个线程显然在争夺数据库连接??我得到一个错误,说“在任何查询之前必须设置事务隔离级别”。我的数据库知识已经到了极限。这里的一个问题似乎表明我不能这样做,但这似乎很难相信,因为我希望通过web连接,完全可能有多个线程同时接触数据库。为了完成这个答案(对我来说很有用!谢谢!),您还应该设置
    sql\u mode='PIPES\u AS\u CONCAT'
    ;如果您不这样做,我的MySQL会将“| |”视为逻辑或最佳点!我在测试PostgreSQL。事实上,
    |
    不是标准的SQL运算符。标准方法是使用
    CONCAT()
    函数,但它不适合此解决方案…请参阅下面的答案,了解使用CONCAT的便携式解决方案!非常有趣的一点(但与问题无关)。您是否有资源讨论如何使用Django处理这种情况?不是理想的可移植性:“但是,SQLite不支持CONCAT()函数”。有时sqlite很方便,例如用于测试。