Django引发运行Model.objects.get的DatabaseError

时间:2012-10-18 12:37:07

标签: django django-models postgresql-9.1 tastypie

我正在使用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有关,但我还没弄清楚如何避免它。

感谢任何帮助。谢谢。

3 个答案:

答案 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