我有一个任务在一个视图上被调用。基本上,该任务负责获取一些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日志中看到它的出现。)
然而,它很快就被覆盖了,我相信在它更新并保存profile_js
属性之后,get_profile_document
视图中的调用事实上,我知道这是基于SQL语句发生的。它在profile_js更新之前就在那里,然后就不见了
现在我真的不明白为什么。扬声器在每次更新和保存之前都会被提取,而且这里还没有真正的缓存,除非get_profile()做了一些奇怪的事情。发生了什么?我该如何避免这种情况?(此外,在fileField上运行save
后,我是否需要在speaker
上调用save?因此,postgres日志中似乎有重复的调用。
更新
可以肯定的是,这是由于Django默认的视图事务处理。该视图开始一个事务,需要很长时间才能完成,然后提交,覆盖我已经在芹菜任务中更新的对象。
我不知道该怎么解决。如果我把方法切换到手动事务,然后在获取echosign js后立即提交(需要5-10秒),它会启动新的事务吗?似乎不起作用。
也许不是
我没有添加TransactionMiddleware
。所以除非它发生,否则这不是问题所在。
已解决
所以问题来了。
Django显然保存了一个对象缓存,它认为这些对象在任何地方都没有更改。(如果我错了,请纠正我。)由于芹菜正在django之外的数据库中更新我的对象,当我说user.get_profile()时,它不知道这个对象已经更改,并将缓存的版本反馈给了我。
强制它从数据库中抓取的解决方案只是用自己的id重新标记它。这有点傻,但它有效。
speaker = user.get_profile()
speaker = Speaker.objects.get(pk=speaker.id)
显然,django的作者不想在对象上添加任何类型的refresh()方法,所以这是下一个最好的方法。
使用事务也可能解决我的问题,但改天再说。
更新
经过进一步的挖掘,这是因为用户模型上有一个_profile_cache
属性,所以它不会在每次从同一对象获取一个请求中的概要文件时都重新进行蚀刻。由于我在同一个对象的echosign函数中使用了get_profile(),所以它被缓存了。