允许 Django 模型中的字段子集中的一个且仅一个字段

Posted

技术标签:

【中文标题】允许 Django 模型中的字段子集中的一个且仅一个字段【英文标题】:Allow one and only one field from fields subset in Django model 【发布时间】:2018-03-12 03:19:21 【问题描述】:

我有一个模型模型。该模型有多个属性,其中三个是:domaintldsubdomainurl - OneToOneFields。

我试图让这些字段中的一个且只有一个不为空。

我的方法有效,但我很好奇是否有更好的方法来做到这一点(我使用 PostgreSQL)。

def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
    self.clean()
    super(Model, self).save(force_insert, force_update, using, update_fields)

def clean(self):
    super(Model, self).clean()

    if not(((bool(self.url) ^ bool(self.domaintld)) ^ bool(self.subdomain)) and not(self.url and self.domaintld and self.subdomain)):
        raise exceptions.ValidationError("One and only one field can be used: url,domaintld,subdomain")

【问题讨论】:

【参考方案1】:

假设您的models.py 中有这样的内容:

class DomainTLD(models.Model):
    # attributes and methods

class Subdomain(models.Model):
    # attributes and methods

class URL(models.Model):
    # attributes and methods

class MyModel(models.Model):
    domaintld = models.OneToOneField(DomainTLD)
    subdomain = models.OneToOneField(Subdomain)
    url = models.OneToOneField(URL)
    # other attributes and methods

我建议使用 django contenttypes 并像这样重构:

class MyModel(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

并废弃 saveclean 方法。现在MyModel 可以与任何模型建立关系,无论是DomainTLDSubdomain 还是URL - content_type 指向模型,object_id 保存对象 ID,content_object,这不会成为数据库中的一个字段,将用于检索对象。

这是一种更简洁的方法,您可以摆脱那种晦涩难懂的clean 方法。 您可以进一步增强它并将content_type 的选项限制为您希望的选项。这个SO answer 解释了这一点。

【讨论】:

以上是关于允许 Django 模型中的字段子集中的一个且仅一个字段的主要内容,如果未能解决你的问题,请参考以下文章

Flutter-如何一次且仅一次渲染listview.builder中的所有项目

允许 Django 模型 ForeignKey 字段的表单上的 TextInput

通过django模板中的外键关系快速访问模型属性

Django:从服务器上的现有文件手动创建模型中的图像字段

模型字段类型从 CharField 更改为 ForeignKey 时 Django 模板损坏

Django 中的唯一模型字段和区分大小写(postgres)