質問

小さなジレンマに遭遇しました!vote というハンドラーがあります。これが呼び出されると、ユーザーの投票がユーザーが選択したものに設定されます。彼らが以前に選択したオプションを記憶するために、現在の投票がどのように設定されているかの詳細を示す VoteRecord オプションを保存します。

もちろん、初めて投票するときは、オブジェクトを作成して保存する必要があります。ただし、連続した投票では、既存の VoteRecord の値が変更されるだけです。しかし、彼は問題を抱えています。状況によっては、2 つの VoteRecord が作成されることがあります。それはまれですが (これまでに確認した 500 票のうち 1 回だけ発生しました)、発生した場合は依然として問題があります。

この問題は、2 つの別個のハンドラーがどちらも本質的にこれを行うために発生します。

query = VoteRecord.all().filter('user =', session.user).filter('poll =', poll)

if query.count(1) > 0:
 vote = query[0]

 poll.votes[vote.option] -= 1
 poll.votes[option] += 1
 poll.put()

 vote.option = option
 vote.updated = datetime.now()
 vote.put()
else:
 vote = VoteRecord()
 vote.user = session.user
 vote.poll = poll
 vote.option = option
 vote.put()

 poll.votes[option] += 1
 poll.put()

 session.user.votes += 1
 session.user.xp += 3
 session.user.put()

 incr('votes')

私の質問は次のとおりです。リクエストが失われず、リクエストが 2 つの VoteRecord オブジェクトを作成しないようにしながら、これらのリクエストを処理する最も効果的かつ最速の方法は何ですか?

役に立ちましたか?

解決

問題はこの部分です:

if vote.count(1) == 0:
    obj = VoteRecord()
    obj.user = user
    obj.option = option
    obj.put()

トランザクションがなければ、コードは 2 つのインタープリター インスタンスで次の順序で実行される可能性があります。

if vote.count(1) == 0:
    obj = VoteRecord()
    obj.user = user


if vote.count(1) == 0:
    obj = VoteRecord()
    obj.user = user
    obj.option = option
    obj.put()


    obj.option = option
    obj.put()

あるいはそれらの奇妙な組み合わせ。問題は、put が発生する前に count テストが再度実行されるため、2 番目のスレッドが条件の 2 番目の部分ではなく最初の部分を通過することです。

この問題は、コードを関数に入れて次を使用することで修正できます。

db.run_in_transaction()

関数を実行します。

問題は、トランザクションに含める必要がある決定ロジックのクエリによって返されるオブジェクトの数に依存しているようです。Google I/O の講演を読んだり、グループを見たりすると、これが推奨されていないことがわかります。これは、クエリをトランザクション化できないためです。代わりに、カウントをエンティティ値としてどこかに保存し、クエリを実行する必要があります。 トランザクション関数のキーを取得し、そのエンティティのキ​​ーをトランザクション関数に渡します。

次に、エンティティ プロパティをチェックするトランザクション関数の例を示します。キーがパラメータとして渡されます。

def checkAndLockPage(pageKey):
  page = db.get(pageKey)
  if page.locked:
    return False
  else:
    page.locked = True
    page.put()
    return True

このエンティティをロックできるのは一度に 1 人のユーザーだけであり、ロックが重複することはありません。

他のヒント

これを行う最も簡単な方法は、あなたの投票オブジェクトのキー名を使用し、Model.get_or_insertを使用することです。投票後にそれを命名することは良いアイデアです - - まず、あなたのキー名の命名スキームを思い付くし、次にフェッチまたは関連するエンティティを作成するget_or_insertを行います:

vote = VoteRecord.get_or_insert(pollname, parent=session.user, user=session.user, poll=poll, option=option)
if vote.option != option:
  # Record already existed; we need to update it
  vote.option = option
  vote.put()
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top