2

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

任何帮助表示赞赏。谢谢你。

4

3 回答 3

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)在数据库中不存在。取一个现有的并分配给它。这不是 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
于 2012-10-18T13:03:22.690 回答
0

Have you tried to manage the transaction yourself ? (see below for an example) It looks like the whole save method is wrapped within a single transaction so that the first query after a query which raised an error will always raise an exception.

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
于 2012-10-18T21:59:08.413 回答
0

捕获 DB 错误,然后在没有发生任何事情的情况下继续对我来说看起来很奇怪。

current transaction is aborted, commands ignored until end of transaction block

如果您想在 postgres 中出现此类错误后继续,则需要使用保存点。

也许Activity.objects.get_or_create(..., defaults=dict(attr=value))你需要的一切。

于 2012-10-18T12:42:40.057 回答