假设我有一个模型,employee
:
员工
CharField
EmailField
ForeignKey
问题出在type
字段上。我希望外键能够几种不同类型的模型。例如,一个用于Software Developer
,另一个用于Janitor
,依此类推。
我可以通过为所有类型设置一个外键字段并将其中许多设置为null来做到这一点,但这似乎很糟糕。这有道理吗?
我该如何实现?谢谢!
答案 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">,]