【问题标题】:Django get_or_create failed with multiprocessing.PoolDjango get_or_create 因 multiprocessing.Pool 失败
【发布时间】:2016-11-18 15:39:29
【问题描述】:

这是我的项目设置,使用标准 Django startproject 命令和单个应用程序:

Python 3.5.1
Django 1.9.7
PostgreSQL 9.5.3
Ubuntu 16.04

应用的 models.py 定义了 2 个模型:

from django.db import models

class A(models.Model):
    n = models.PositiveIntegerField(primary_key=True)

class B(models.Model):
    a = models.ForeignKey(A, blank=True, null=True)
    m = models.CharField(max_length=20, db_index=True)

    class Meta:
        unique_together = ('a', 'm')

    def __str__(self):
        return '%s' % self.m

这是我的名为 execute.py 的管理命令,用于创建 B 的实例:

from multiprocessing import Pool
from django import db
from django.core.management.base import BaseCommand
from .models import B

M = 'abcdef'

def create():
    obj, created = B.objects.get_or_create(m=M, defaults={'a': None})
    if created:
        print('obj=%s' % obj)

class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        B.objects.filter(m=M).delete()
        db.connections.close_all()
        n = 4
        pool = Pool(processes=n)
        results = []
        for _ in range(n):
            result = pool.apply_async(create)
            results.append(result)
        pool.close()
        for result in results:
            result.get()
        pool.join()

运行 python manage.py execute 会导致创建 4 个 obj,而不仅仅是 1 个。但情况并非总是如此。有时,只创建了 3 个甚至 1 个对象:

obj=abcdef
obj=abcdef
obj=abcdef
obj=abcdef

我是否遗漏了一些东西来强制这里的唯一性?

【问题讨论】:

标签: python django postgresql multiprocessing


【解决方案1】:

documentation for get_or_create() 中解释了此行为:

假设正确使用、正确的数据库配置和正确的底层数据库行为,此方法是原子的。 但是,如果在数据库级别没有强制唯一性 [...],这种方法很容易出现竞争条件,这可能会导致多行具有相同的参数同时插入。

B 模型的 m 字段没有 unique=True 约束,这会导致竞争条件。将字段定义更改为:

m = models.CharField(max_length=20, db_index=True, unique=True)

你应该没事的。

【讨论】:

  • m 字段中添加unique=True 有效。但是如果m 的值可能重复,并且只有am 需要按照models.py 中的规定是唯一的,该怎么办?
  • @traceback:在这种情况下,您需要实现自己的同步机制。你甚至不能使用自己的 SQL 语句:Postgresql 提供 INSERT ... ON CONFLICT DO NOTHING,但要在你的情况下工作,需要有冲突(即,必须有一个 UNIQUE 列)
  • 这个索引是在 PostgreSQL "app_b_a_id_0f0268ca_uniq" UNIQUE CONSTRAINT, btree (a_id, m) 迁移后创建的。我很好奇为什么get_or_create 仍然尝试在给定UNIQUE CONSTRAINT 的情况下创建一个新对象。
  • 这是对B.a 的约束,而不是对B.m 的约束
  • 好的,我认为使用 NULL 值的defaults 是这里的cuplrit:obj, created = B.objects.get_or_create(m=M, defaults={'a': None})。在defaults 中使用A 的实例时不会出现此问题。
猜你喜欢
  • 2016-08-01
  • 2020-05-28
  • 2013-11-06
  • 2020-01-17
  • 2012-03-07
  • 2015-06-09
  • 2016-04-07
  • 2012-02-24
  • 2016-01-01
相关资源
最近更新 更多