0

我有一个在一个视图上调用的任务。基本上,该任务负责获取一些 pdf 数据,并通过 django 存储将其保存到 s3 中。

这是启动它的视图:

@login_required
@minimum_stage(STAGE_SIGN_PAGE)
def page_complete(request):
    if not request.GET['documentKey']:
        logger.error('Document Key was missing', exc_info=True, extra={
            'request': request,
        })
    user = request.user
    speaker = user.get_profile()
    speaker.readyForStage(STAGE_SIGN)
    speaker.save()
    retrieveSpeakerDocument.delay(user.id, documentKey=request.GET['documentKey'], documentType=DOCUMENT_PAGE)
    return render_to_response('speaker_registration/redirect.html', {
        'url': request.build_absolute_uri(reverse('registration_sign_profile'))
    }, context_instance=RequestContext(request))

这是任务:

@task()
def retrieveSpeakerDocument(userID, documentKey, documentType):
    print 'starting task'
    try:
        user = User.objects.get(pk=userID)
    except User.DoesNotExist:
        logger.error('Error selecting  user while grabbing document', exc_info=True)
        return
    echosign = EchoSign(user=user)
    fileData = echosign.getDocumentWithKey(documentKey)
    if not fileData:
        logger.error('Error retrieving document', exc_info=True)
    else:
        speaker = user.get_profile()
        print speaker
        filename = "%s.%s.%s.pdf" % (user.first_name, user.last_name, documentType)
        if documentType == DOCUMENT_PAGE:
            afile = speaker.page_file
        elif documentType == DOCUMENT_PROFILE:
            afile = speaker.profile_file

        content = ContentFile(fileData)
        afile.save(filename, content)
        print "saving user in task"
        speaker.save()

与此同时,我的下一个视图命中(实际上它是一个 ajax 调用,但这没关系)。基本上它为下一个嵌入式文档获取代码。一旦它得到它,它就会更新扬声器对象并保存它:

@login_required
@minimum_stage(STAGE_SIGN)
def get_profile_document(request):
    user = request.user
    e = EchoSign(request=request, user=user)
    e.createProfile()
    speaker = user.get_profile()
    speaker.profile_js = e.javascript
    speaker.profile_echosign_key = e.documentKey
    speaker.save()
    return HttpResponse(True)      

我的任务正常工作,并speaker.page_file正确更新属性。(我可以暂时在管理员中看到这个,也可以在 postgres 日志中看到它。)

但是它很快就被盖住了,我相信get_profile_document在它更新并保存profile_js属性后视图中的调用。事实上,我知道这是基于 SQL 语句发生的地方。在 profile_js 更新之前它就在那里,然后它就消失了。

现在我真的不明白为什么。扬声器在每次更新和保存之前被正确获取,并且这里还没有真正的缓存,除非 get_profile() 做了一些奇怪的事情。发生了什么事,我该如何避免这种情况?(另外,我需要在 fileField 上speaker运行后调用 save onsave吗?因此,postgres 日志中似乎有重复的调用。

更新

很确定这是由于 Django 的默认视图事务处理。视图开始一个事务,需要很长时间才能完成,然后提交,覆盖我已经在 celery 任务中更新的对象。

我不确定如何解决它。如果我将方法切换为手动事务,然后在获取 echosign js 后立即提交(需要 5-10 秒),它会启动新事务吗?似乎没有工作。

也许不吧

我没有TransactionMiddleware添加。所以除非它发生了,否则这不是问题。

4

1 回答 1

1

解决了。

所以这就是问题所在。

Django 显然保留了一个它认为在任何地方都没有改变的对象的缓存。(如果我错了,请纠正我。)由于 celery 在 django 之外的数据库中更新我的对象,所以当我说 user.get_profile() 时,它不知道这个对象已经改变并把缓存的版本反馈给我。

强制它从数据库中抓取的解决方案就是用它自己的 id 重新抓取它。它有点傻,但它有效。

speaker = user.get_profile()
speaker = Speaker.objects.get(pk=speaker.id)

显然 django 作者不想在对象上添加任何类型的 refresh() 方法,所以这是下一个最好的事情。

使用事务也可以解决我的问题,但改天。

更新

经过进一步挖掘,这是因为用户模型上有一个_profile_cache属性,因此每次您从同一个对象的一个​​请求中获取配置文件时,它都不会重新获取。由于我在同一个对象的 echosign 函数中使用 get_profile() ,因此它被缓存了。

于 2012-08-21T06:19:56.233 回答