2011-11-15 11 views
21

Djangoビューでは、データベースの既存のテキスト列の最後に文字列データを追加する必要があります。たとえば、「ATable」という名前のテーブルがあり、「aField」という名前のフィールドがあるとします。私は競争条件のない方法で "aField"の最後に文字列を追加できるようにしたいと思います。当初、私はこれを持っていた:Django:更新呼び出しのテキストフィールドにF式を使用する

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バグ報告を見つけましたが、修正または回避策は見当たりませんでした。 FString式のテキストに追加できるように、aStringVarをキャストできる方法はありますか? BTW - "str(F( 'aField'))+ aStringVar"を試しましたが、F式の結果を文字列 "(DEFAULT:)"に変換しました。

+0

に変更されました。sqlquery Djangoが実行しようとしていますか? – Willian

+1

[文字列連結でDjango F()オブジェクトを使用することはできますか?](http://stackoverflow.com/questions/3300944/can-i-use-django-f-objects-with-string-concatenation) – Alasdair

+0

ダニエルのもう一つの質問についての説明はかなり確定していると思います。あるいは、[custom sql](https://docs.djangoproject.com/ja/dev/topics/db/sql/#executing-custom-sql-directly)を書いて更新を実行することもできます。 – Alasdair

答えて

2

これはできないようです。しかし、あなたがやろうとしているものがtransactions

を使用して解決することができた(あなたは1つのクエリでそれを行うと示唆されているように、生のSQLを使用したいので、もしあなたが、Postgresのを使用しているように見える、||はあなたが欲しい連結演算子です)

3

これを実行すると、スレッドセーフではありません。更新が実行されている間、他の一部のプロセスは、データベース内のデータが更新されていることを知らないモデルを更新することができます。

あなたもロックを取得しているが、これsenarioを忘れないでください

  1. ジャンゴ:M = Model.objects.all()[10]
  2. ジャンゴ:m.field =フィールド
  3. ジャンゴ:時間がかかる進歩(time.sleep(100))
  4. DB:ロックテーブル
  5. DB:Updateフィールド
  6. DD:ロック解除テーブル
  7. ジャンゴ:M:遅いプロセスが
  8. ジャンゴ終了します。セーブ()

は今、フィールドの更新は、あなたがDjangoのselect_for_update()演算子でこの機能を実現することができますジャンゴでのモデルインスタンス(ゴーストライト)

+0

非常に興味深い点です(質問には実際には関係ありません)。この種の状況をDjangoで処理する方法について議論するリソースがありますか? – lajarre

3

によって取り消さになりました。このようなhttps://docs.djangoproject.com/en/dev/ref/models/querysets/#select-for-update

何か:

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

あなたは.select_for_update(呼び出すときにテーブルの行)がロックされます。取得する()、およびあなたが.SAVE(呼び出したときにロック)が解除されます、あなたが実行できるようにします原子的に操作する。

22

あなたは1つの簡単な変更とジャンゴにFオブジェクトを上書きすることができます。

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

それからちょうどFの代わりにCFを使用しています。それは "||" SQLを生成するときは "+"の代わりに使用します。たとえば、クエリ:

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

は、SQLを生成します。

UPDATE "auth_user" SET "email" = "auth_user"."username" || '@gmail.com' 
WHERE "auth_user"."id" = 100 
+1

ちょうどこの答えを完了するために(私のために働いた!ありがとう!) 'sql_mode = 'PIPES_AS_CONCAT''も設定する必要があります。もしあなたがそれをしなければ、私のMySQLは "||"論理としてOR –

+1

良い点!私はPostgreSQLをテストしていました。実際、 '||'は標準のSQL演算子ではありません。標準的な方法は 'CONCAT()'関数を使うことですが、この解決法には適していません... –

+0

Concatを使用したポータブルソリューションについては、以下の回答を参照してください。 –

15

あなたはConcatデシベル機能を使用することができます。

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(ジャンゴ1.9)を取得::私の場合は

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の接尾辞を追加してい

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

結果すべての画像URIはhttp://graph.facebook.com/<fb user id>/pictureからhttp://graph.facebook.com/<fb user id>/picture?type=large

+1

これは、Django 1.8以降に行う正しい方法です – Fush

関連する問題