我正在使用Django 1.4,postgres 9.1.x和Tastypie 0.9.11来创建API。
我定义了以下模型:
class Activity(models.Model):
company = models.ForeignKey(Company)
opportunity = models.ForeignKey(Opportunity)
name = models.CharField(max_length=200)
...
我正在覆盖“保存”方法,以便在此过程中执行一些特殊操作:
def save(self, *args, **kwargs):
saveResult = None
try:
saveResult = super(Activity, self).save(*args, **kwargs)
except IntegrityError, e:
logger.error('IntegrityError %s' % e.message)
if(not self.pk):
try:
self = Activity.objects.get(company_id=self.company.id,opportunity_id=self.opportunity.id)
except ObjectDoesNotExist, e:
pass
except DatabaseError, e:
logger.error('DatabaseError %s' % e.message)
if(self.pk and not self.notified):
send_notification(...)
self.notified = 1
self.save()
return saveResult
下面我描述一下我正在做的POST请求的示例:
curl --dump-header - -H "Content-Type: application/json" -X POST -d '{"company": {"id":10},"opportunity": {"id":39}}' http://127.0.0.1:8001/api/v1/activity/
这个过程适用于新对象,但是当我对现有行执行POST请求时(我之前没有办法知道它是否是新的),我当然得到了IntegrityError由于重复键错误,这是正确的。
然后,我想用存储在现有行中的信息填充自我数据,这就是我执行以下查询的原因(我没有找到更优雅的方式来做,如果你有更好的选择将是理解):
自我= Activity.objects.get(COMPANY_ID = self.company.id,opportunity_id = self.opportunity.id)
问题是该进程正在死亡,因为“get”行正在抛出一个DatabaseError,其中包含“当前事务被中止,命令被忽略直到事务块结束”,以避免获取现有对象。
这些是我在屏幕上打印的消息:
2012-10-18T04:57:27+00:00 app[web.1]: .............Saving new activity.............
2012-10-18T04:57:27+00:00 app[web.1]: Company: 10, video test | Opportunity: 39 | Activity: , created: None, None, 1,
2012-10-18T04:57:27+00:00 app[web.1]: IntegrityError duplicate key value violates unique constraint "db_activity_company_id_1276b66f55cae366_uniq"
2012-10-18T04:57:27+00:00 app[web.1]: DETAIL: Key (company_id, opportunity_id)=(10, 39) already exists.
2012-10-18T04:57:27+00:00 app[web.1]: DatabaseError current transaction is aborted, commands ignored until end of transaction block
顺便说一句:我在MySql中尝试过完全一样,效果很好。
所以,我不知道为什么“get”方法会产生错误。我想这可能与前面提到的IntegrateError有关,但我还没弄清楚如何避免它。
感谢任何帮助。谢谢。
答案 0 :(得分:2)
我不明白这里的逻辑。查看此行的代码
if(not self.pk):
try:
self = Activity.objects.get(company_id=self.company.id,opportunity_id=self.opportunity.id)
except ObjectDoesNotExist, e:
pass
except DatabaseError, e:
logger.error('DatabaseError %s' % e.message)
您说:如果数据库中不存在当前对象(self)。拿一个现有的并分配给它。这不是自我的工作方式。
您应首先检查数据库中是否存在记录。如果存在,则将其分配给局部变量。如果它不存在,请保存它,然后将其添加到同一个局部变量。
def save(self, *args, **kwargs):
try:
saveResult = Activity.objects.get(company_id=self.company.id,opportunity_id=self.opportunity.id)
except ObjectDoesNotExist, e:
#Object does not exist. Add it
saveResult = super(Activity, self).save(*args, **kwargs)
if not saveResult.notified:
send_notification(...)
saveResult.notified = 1
saveResult.save()
return saveResult
答案 1 :(得分:0)
捕获数据库错误然后继续,如果没有发生任何事情对我来说很奇怪。
current transaction is aborted, commands ignored until end of transaction block
如果你想在postgres中发生这样的错误后继续使用,你需要使用保存点。
也许Activity.objects.get_or_create(..., defaults=dict(attr=value))
可以满足你所需要的一切。
答案 2 :(得分:0)
您是否尝试过自己管理交易? (见下面的例子) 看起来整个save方法都包含在一个事务中,因此查询引发错误后的第一个查询将始终引发异常。
def save(self, *args, **kwargs):
with transaction.commit_manually:
saveResult = None
sid = transaction.savepoint()
try:
saveResult = super(Activity, self).save(*args, **kwargs)
transaction.savepoint_commit(sid)
except IntegrityError, e:
logger.error('IntegrityError %s' % e.message)
transaction.savepoint_rollback(sid)
transaction.commit()
if(not self.pk):
try:
self = Activity.objects.get(company_id=self.company.id,opportunity_id=self.opportunity.id)
except ObjectDoesNotExist, e:
pass
except DatabaseError, e:
logger.error('DatabaseError %s' % e.message)
if(self.pk and not self.notified):
send_notification(...)
self.notified = 1
self.save()
return saveResult