2016-09-20 3 views
0

私は今、私はインスタンスItem(current_site, next_id_on_site)を作成したいDjangoモデルのauto incrementフィールドを作成するには、unique_togetherの2つのキーを使用しますか?

next_id_on_site = Item.objects.filter(site=current_site).aggregate(current_id=Max("id_on_site"))['current_id']+1 

に問題がそう、IDを生成し、Itemを作成する操作はアトミックではないこと、である

class Item(models.Model): 
    site = Site() 
    id_on_site = PositiveIntegerField() 

ようなモデルを持っています重複したIDを作成する競合状態があるため、.get(site=current_site, id_on_site=someid)MultipleObjectsReturned例外を発生させます。

モデル内でunique_togetherを使用すると、自動インクリメントIDの生成には役立たず、DBレベルではまったく実装されていないようです。

+0

id_on_siteはIDを自動生成していますか? –

答えて

1

unique_togetherがデータベースに確実に実装されていますが、一意のデータベースインデックスが生成されるため、その効果を確認するには移行を実行する必要があります。

あなたが望むすべてのは、サイトでのアイテムのいくつかのユニークな識別子であることをid_on_siteためである場合は、一意のほぼ一定の保証を持っている、UUIDField(default=uuid.uuid4)ようなものを使用する方が簡単かもしれません。 IDを自動インクリメント整数にする必要がある場合は、少し難しくなります。

競合状態を避けるための1つのオプションは、最大値がid_on_siteの行をロックすることです。これは、彼らが最高のid_on_siteを取得しようとしている場合はブロックするように他のトランザクションを起こさなければならない

from django.db.transaction import atomic 

with atomic(): 
    next_id_on_site = (
     Item.objects 
     .filter(site=current_site) 
     .select_for_update(nowait=False) 
     .latest('id_on_site').id_on_site) 
    Item.objects.create(current_site, next_id_on_site) 

、およびトランザクションがコミットされると、挿入した項目は、次のようになります(これはcertain database backends,例えばpostgresの上で動作します)他の取引に戻りました。これは、トランザクションが何らかの理由で長寿命になると問題になる可能性があります。

+0

私は増分する整数を使用したいと思います。これは、サイトのユーザーに表示したい数値です。私はアトミックで試しましたが、私はそれがmysqlで動作しないと思います。私の現在の試みは、無効なidを持つアイテムを保存し、次に最大IDを集計し、 '' item.objects.filter(id = newitem.id).update(id_on_site = new_id) ''を実行することです。 .save() ''、しかし、これはまだ集約と更新の間の競合状態を引き起こします。ドキュメントは、 '' select_for_update''のように、postresのような素晴らしいトランザクションがない場合でもmysqlのために動作するはずです。やってみます。 – allo

+0

アトミックは、両方のデータベースバックエンドで動作しますが、競合状態は解決しません。ロックの方法がうまくいかない場合は、行を更新するための生のSQLクエリを書く価値があるかもしれません。 DjangoのORMで表現するのは難しい単純なSQL文でなければなりません。 – RecursivelyIronic

+0

更新については、 '' .filter(...)。update(...) ''行は単に '' UPDATE ... WHERE ... ''文を作成するだけです。 – allo

関連する問題