我有一个在一个视图上调用的任务。基本上,任务负责获取一些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
上调用save?看来postgres日志中存在重复调用。
很确定这是由于Django的默认视图事务处理。视图开始一个事务,需要很长时间才能完成,然后提交,覆盖我已经在芹菜任务中更新过的对象。
我不确定如何解决它。如果我将方法切换到手动事务,然后在我获取echosign js(需要5-10秒)后立即提交,它是否会启动新事务?似乎没有用。
我没有添加TransactionMiddleware
。所以除非它发生了,否则这不是问题。
答案 0 :(得分:1)
所以这就是问题所在。
Django显然保留了一些它认为在任何地方都没有改变的对象缓存。 (如果我错了,请纠正我。)因为芹菜在django之外的数据库中更新了我的对象,所以当我说user.get_profile()时,它不知道这个对象已经改变并且给我提供了缓存版本。
强制它从数据库中获取的解决方案只是用自己的id重新调整它。它有点傻,但它确实有效。
speaker = user.get_profile()
speaker = Speaker.objects.get(pk=speaker.id)
显然django作者不想在对象上添加任何类型的refresh()方法,所以这是下一个最好的事情。
使用交易也可以解决我的问题,但是另一天。
进一步挖掘之后,因为用户模型上有一个_profile_cache
属性,因此每次从同一对象的一个请求中获取配置文件时它都不会重新获取。因为我在同一个对象的echosign函数中使用了get_profile(),所以它被缓存了。