Django: Using an F expression for a text field in an update call

后端 未结 5 958
小鲜肉
小鲜肉 2021-01-01 10:17

In a django view, I need to append string data to the end of an existing text column in my database. So, for example, say I have a table named \"ATable\", and it has a fiel

相关标签:
5条回答
  • 2021-01-01 10:58

    seems you can't do this. however, what you are trying to do could be solved using transactions

    (looks like you are using postgres, so if you want to do it in one query and use raw sql as suggested, || is the concatenation operator you want)

    0 讨论(0)
  • 2021-01-01 10:59

    You can override F object in Django with one simple change:

    class CF(F):
        ADD = '||'
    

    Then just use CF in place of F. It will place "||" instead of "+" when generating SQL. For example, the query:

    User.objects.filter(pk=100).update(email=CF('username') + '@gmail.com')
    

    will generate the SQL:

    UPDATE "auth_user" SET "email" = "auth_user"."username" || '@gmail.com'
    WHERE "auth_user"."id" = 100 
    
    0 讨论(0)
  • 2021-01-01 11:12

    You can use the Concat db function.

    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')))
    

    In my case, I am adding a suffix for facebook avatars URIs like this:

    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)))
    

    and I get SQL like this (Django 1.9):

    UPDATE `user_user` SET `avatar_uri` = CONCAT(COALESCE(`user_user`.`avatar_uri`, ''), COALESCE('?type=large', ''))
    WHERE (`user_user`.`avatar_uri` LIKE '%graph.facebook.com%' AND NOT (`user_user`.`avatar_uri` LIKE '%?type=large%' AND `user_user`.`avatar_uri` IS NOT NULL))
    

    The result is all image URIs were changed from http://graph.facebook.com/<fb user id>/picture to http://graph.facebook.com/<fb user id>/picture?type=large

    0 讨论(0)
  • 2021-01-01 11:15

    And if you get this running, it isn't thread safe. While your update is running, some other process can update a model not knowing the data in the database is updated.

    You have too acquire a lock, but don't forget this senario:

    1. Django: m = Model.objects.all()[10]
    2. Django: m.field = field
    3. Django: a progress which takes a while (time.sleep(100))
    4. DB: Lock table
    5. DB: Update field
    6. DD: Unlock table
    7. Django: the slow process is finished
    8. Django: m.save()

    Now the field update became undone by the model instance in Django (Ghost write)

    0 讨论(0)
  • 2021-01-01 11:21

    You can achieve this functionality with Django's select_for_update() operator: https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-for-update

    Something like this:

    obj = ATable.objects.select_for_update().get(id=100)
    obj.aField = obj.aField + aStringVar
    obj.save()
    

    The table row will be locked when you call .select_for_update().get(), and the lock will be released when you call .save(), allowing you to perform the operation atomically.

    0 讨论(0)
提交回复
热议问题