【问题标题】:How can I constrain or choose between one or other foreign key?如何在一个或其他外键之间进行约束或选择?
【发布时间】:2020-12-18 15:47:08
【问题描述】:

问题是概念性的(数据库关系),所以语言不是这里的重点,但我使用的是 Python 和 Django。

我有 3 个模型/表格:

  • 公司
  • 客户
  • 地址

例如。

class Company(models.Model):
    name = models.CharField(max_lenght=100) #example

class Customer(models.Model):
    name = models.CharField(max_lenght=100) #example

class Adress(models.Model):
    country = models.CharField(max_lenght=100) #example
    state = models.CharField(max_lenght=100) #example
    
    # here I want the address owner
    # I could put something like this:
    company = models.ForeignKey(Company, on_delete=models.PROTECT)
    customer = models.ForeignKey(Customer, on_delete=models.PROTECT)

我希望地址属于客户或公司,但不能同时属于两者。
我知道我可以简单地创建 2 个地址类,例如 CustomerAddress、CompanyAddress,每个类都有其正确的外键:

class Company(models.Model):
    name = models.CharField(max_lenght=100) #example

class Customer(models.Model):
    name = models.CharField(max_lenght=100) #example

class CompanyAdress(models.Model):
    country = models.CharField(max_lenght=100) #example
    state = models.CharField(max_lenght=100) #example
    company = models.ForeignKey(Company, on_delete=models.PROTECT)

class CustomerAdress(models.Model):
    country = models.CharField(max_lenght=100) #example
    state = models.CharField(max_lenght=100) #example
    customer = models.ForeignKey(Company, on_delete=models.PROTECT)

但我不想这样做有两个原因:

  1. 重复的代码以及在 Django 管理面板中我将有两个单独的地址列表这一事实,这没有多大意义,因为所有地址在结构上都是相同的。我可以修复创建基类、继承基类等的重复代码,但我仍然会在管理面板中有 2 个列表。
  2. 将来我可能会遇到同样的概念问题,但会更复杂,例如300 个类的某些东西和 1 个类应该只有 300 个中的一个的外键。

我该怎么办?

【问题讨论】:

  • 为什么不在 Company 和 Customer 的模型中添加一个外键 address 字段?
  • 搜索party model。常见的模式。或者超类型,子类型stackoverflow.com/…

标签: python django database database-design foreign-keys


【解决方案1】:

有关 UML 图,请查看 Martin Fowler 的 Analysis Patterns。下面是一个DB模型。

-- Address ADR exists.
--
address {ADR}
     PK {ADR}

Party是个人或组织的总称;鉴别器TYP用于区分两者。

-- Party PTY, of party-type TYP, resides at address ADR.
--
party {PTY, TYP, ADR}
   PK {PTY}
   SK {PTY, TYP}

CHECK TYP in {'P', 'O'}

FK {ADR} REFERENCES address {ADR}
-- Person, a party PTY of party-type TYP = 'P', exists.
--
person {PTY, TYP}
    PK {PTY}

CHECK TYP = 'P'

FK {PTY, TYP} REFERENCES party {PTY, TYP}
-- Organization, a party PTY of party-type TYP = 'O', exists.
--
organization {PTY, TYP}
          PK {PTY}

CHECK TYP = 'O'

FK {PTY, TYP} REFERENCES party {PTY, TYP}

注意:

All attributes (columns) NOT NULL


PK = Primary Key
AK = Alternate Key   (Unique)
SK = Proper Superkey (Unique)
FK = Foreign Key

关于子类型的一句话。实现子类型约束的正确方法是使用断言 (CREATE ASSERTION),但它在主要数据库中仍然不可用。我改用FKs,与所有其他替代方法一样,它并不完美。人们争论很多,关于 SO 和 SE-DBA,什么更好。我鼓励您也检查其他方法。

【讨论】:

    【解决方案2】:

    您可以尝试将外键设置为 null=True 并根据需要通过视图控制数据库中的输入:

    class Address(models.Model):
        country = models.CharField(max_lenght=100) 
        state = models.CharField(max_lenght=100)
        company = models.ForeignKey(Company, on_delete=models.PROTECT, null=True)
        customer = models.ForeignKey(Customer, on_delete=models.PROTECT, null=True)
    

    【讨论】:

    • 我认为的正确答案?
    猜你喜欢
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 2014-08-30
    • 1970-01-01
    • 2012-03-30
    相关资源
    最近更新 更多