【问题标题】:Apply unique constraint to columns on two separate tables (Django, postgres)对两个单独表(Django、postgres)上的列应用唯一约束
【发布时间】:2019-03-21 05:10:42
【问题描述】:

所以我正在组建一个有点像 github 的网站,用户和组织都有一个句柄:

class User(AbstractUser):
    """ Using django's default user model, with its username field which looks like:

        username = models.CharField(
            _('username'),
            max_length=150,
            unique=True,
            help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
            validators=[username_validator],
            error_messages={
                'unique': _("A user with that username already exists."),
            },
        )
    """

    # Many users to many organisations
    organisations = ManyToManyField('Organisation', related_name='users', blank=True)

class Organisation(models.Model):

    # ...other stuff, id etc
    handle = CharField(unique=True, max_length=36, null=False)

我将设置带有mydomain.com/username_or_handle/stuff 等端点的网址。 这会产生冲突,因为用户可能拥有与组织相同的句柄。

当前解决方案:

我有一个信号,在预先保存用户或组织时,会同时查找两个表以确保唯一性,如果违反,则会引发 ValidationError

@receiver(pre_save, sender=User)
@receiver(pre_save, sender=Organisation)
def check_valid_handle(sender, instance, **kwargs):
    """ The abbreviated gist...
    """
    if (Organisation.objects.filter(handle=instance.handle).count() > 0) or (User.objects.filter(username=instance.handle).count() > 0):
        raise ValidationError(detail='This handle is already taken, or prohibited. Please try another.'.format(type_str))

但严格来说,这可能会受到竞争条件的影响,因为在检查时间和模型创建/更新时间之间没有应用锁定。此外,它感觉很hacky。

是否可以在数据库级别应用此约束,而无需从根本上更改我的模型以进行多表继承或多态(如果不进行大量手术,使用 AbstractUser 进行工作可能是一场噩梦)?如果有,怎么做?

【问题讨论】:

  • 你看过multi-table inheritence吗?我不确定它是否会和AbstractUser一起玩得很好
  • RE:“没有从根本上改变我的模型以从相同的基础继承”。不确定数据库级别。但是您可以使用模型表单或自定义表单上的字段级验证上的验证轻松解决此问题。
  • 还有,为什么要检查计数是否大于0?如果它大于 0,它将评估为 True。所以 if 条件可能只是“ if (Organisation.objects.filter(handle=instance.handle).count() 或 (User.objects.filter(username=instance.handle).count())”
  • @ChetanGanji 是的,谢谢,关于 > 0 完全正确。旧的 C++ 习惯很难改掉。对模型表单(或 DRF 序列化器或其他)进行验证会起作用,但会遇到与我当前需要分别检查两个表的信号相同的问题。
  • @bdoubleu 是的,我有(还有多态性)。这绝对是实现我想要的一种方式,但它是对数据库模式的相当大的手术,正如你所说,我担心 AbstractUser。这就是我所说的“从同一个基础继承”的意思——经过编辑以澄清问题。谢谢!

标签: django python-3.x postgresql unique-constraint


【解决方案1】:

几年后,我注意到这个问题有一些活动,所以想发布一个准答案。

因此,严格的答案(如 cmets 中出现的那样)是“不”,在您的数据库架构中执行此操作并不简单。

我走过的路线

就我而言,我从 django auth 的 Group 模型派生了 Organisation

我还创建了一个UserGroup,它派生自Group,并与用户建立了1:1的关系。

然后,我简单地使用 Group 的 name 字段作为句柄,并在此处使用唯一约束。任务完成。

我想的另一件事

这在我写原始问题时不可用,但 django 现在有更多 powerful constraints system 可用于在创建或更新模型时应用此类约束。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    • 2013-01-21
    • 2015-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多