繁体   English   中英

Django:国外领域的unique_together

[英]Django: unique_together for foreign field

对于接下来的两个模型:

class Foo(models.Model):
    parent = models.ForeignKey(Parent)
    name = models.CharField()


class Bar(models.Model):
    foreign = models.ForeignKey(Foo)
    value = models.CharField(max_length=20)

我需要对Bar模型具有unique_together约束:

class Meta:
    unique_together = ('value', 'foreign__parent')

在Django中这是不可能的。

但是是否有可能在数据库级别(Postgresql)上通过某些约束或模型级别验证来实现这一点,从而在将相同的value同时保存到不同的Bar实例时省略可能的情况(锁定表?)?

的Django 2.2.4

您无法使用unique_together实现它,因为它会在数据库级别创建索引。 但是,您可以自己添加验证,只需覆盖validate_unique方法并将此验证添加到其中即可。

from django.core.exceptions import ValidationError

class Bar(models.Model):
    foreign = models.ForeignKey(Foo)
    value = models.CharField(max_length=20)

    def validate_unique(self, *args, **kwargs):
        super(MyModel, self).validate_unique(*args, **kwargs)

        if self.__class__.objects.\
                filter(foreign__parent=self.foreign.parent, vaue=self.value).exists():
            raise ValidationError(
                message='record already exists with given values.',
                code='unique_together',
            )

感谢Anjaneyulu Batta ,提出了下一个解决方案:

@contextmanager
def lock_table(model):
    """
    Lock target table on commands UPDATE, DELETE and INSERT
    """
    with transaction.atomic(), transaction.get_connection().cursor() as cursor:
        cursor.execute(
            f'LOCK TABLE {model._meta.db_table} IN ROW EXCLUSIVE MODE;'
        )
        yield

对于模型:

def validate_unique(self, exclude=None):
    super().validate_unique(exclude)
    queryset = type(self).objects.filter(
        value=self.value,
        foreign__parent=self.foreign.parent,
    )

    if self.pk:
        queryset = queryset.exclude(pk=self.pk)

    if queryset.exists():
        raise IntegrityError(_('Value must be unique for foreign field'))

def save(self, force_insert=False, force_update=False, using=None,
         update_fields=None):
    with lock_table(type(self)):
        self.validate_unique()
        super().save(force_insert, force_update, using, update_fields)

应该可以正常保存。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM