外键中的许多型号

时间:2020-07-21 17:12:54

标签: django django-models

假设我有一个模型,employee

员工

  • 名称:CharField
  • 电子邮件:EmailField
  • 类型:ForeignKey

问题出在type字段上。我希望外键能够几种不同类型的模型。例如,一个用于Software Developer,另一个用于Janitor,依此类推。

我可以通过为所有类型设置一个外键字段并将其中许多设置为null来做到这一点,但这似乎很糟糕。这有道理吗?

我该如何实现?谢谢!

1 个答案:

答案 0 :(得分:1)

有很多方法可以将这种多态水平建模到这种情况下。

选项1-混凝土基础模型

包括一个employee_type字段,该字段指示正在使用的具体模型。

class Employee(models.Model):
    name = models.CharField(max_length=50)
    email = models.CharField(max_length=80)
    employee_type = models.CharField(max_length=20)

class Janitor(Employee):
    [...]

class SoftwareEngineer(Employee):
    [...]

所有共有属性都可以存储在Employee模型中。

实例化一个具体类(软件工程师,Janitor等)后,它将继承其父类的所有属性。

设置和使用employee_type可以区分创建哪个具体类。

有关更多信息,请参见here

[更新]

使用Django的 Signal 信号,可以派生具体的类名称,并将其与关联的实例一起存储。

signals.py

from django.db.models.signals import pre_save

def store_classname(sender, instance, **kwargs):
    instance.employee_type = instance.__class__.__name__

for subclass in Employee.__subclasses__():
    pre_save.connect(store_classname, sender=subclass)

这可确保每次都存储正确的标识符。

现在在您要选择要使用的具体类的类型的视图中,您可以在如下情况下继续使用它:

views.py

#EXAMPLE USAGE
employee = Employee.objects.get(id=1)
if employee.employee_type == 'Janitor':
    employee = Janitor.objects.get(id=employee.id)

或者通过使用模型名称和对全局变量的查找来动态派生它。

#EXAMPLE USAGE
from <you_app>.models import *
def get_class(classname):
    cls = globals()[classname]
    return cls

employee = Employee.objects.get(id=1)
concrete_employee = get_class(employee.employee_type)
[...]

注意:更改父模型或子模型的名称时要小心,因为这会影响使用旧模型名称的历史记录。要解决此问题,请使用Django的update()bulk_update函数将所有旧名称转换为新名称。更多信息是here

选项2- django-polymorphic

使用名为django-polymorphic的django程序包,当查询父类时,这将返回所有具体的类。

models.py

from polymorphic.models import PolymorphicModel

class Employee(PolymorphicModel):
    EMPLOYEE_TYPE_CHOICES = (
        ('Janitor', 'Janitor'),
        ('Software Engineer', 'Software Engineer'),
    )
    name = models.CharField(max_length=50)
    email = models.CharField(max_length=80)

class Janitor(Employee):
    [...]

class SoftwareEngineer(Employee):
    predominant_programming_language= models.CharField(max_length=100)
    [...]

Employee模型上进行查询时,将返回以下内容

>>> Employee.objects.filter(id=1)
[ <Employee:         id 1, name "Joe Bloggs", email "example@example.com">,
  <SoftwareEngineer:  id 1, name "Joe Bloggs", email "example@example.com", predominant_programming_language "Python">,]
相关问题