【发布时间】: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