问题描述
假设我有一个模型,员工
:
员工
- 名称:
CharField
- 电子邮件:
EmailField
- 类型:
ForeignKey
- Name:
CharField
- Email:
EmailField
- Type:
ForeignKey
问题出在 type
字段。我希望外键能够几种不同类型的模型。例如,一个用于 Software Developer
,另一个用于 Janitor
,依此类推。
The problem is with the type
field. I want the foreign key to be able to be several different types of models. For example, one for Software Developer
, another for Janitor
, and so on.
我可以通过为所有类型设置一个外键字段并将其中许多设置为null来做到这一点,但这似乎很糟糕。
如何实现?谢谢!!!!
How can I achieve this? Thanks!!
推荐答案
有很多方法可以将这种多态水平建模到这样的场景中。
There are many ways you can model this level of polymorphism into a scenario like this.
选项1-混凝土基础模型
OPTION 1 - Concrete Base Model
包括 employee_type
字段,指示正在使用的具体模型。
Including a employee_type
field which indicates the concrete model being used.
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
模型中。
All mutual attributes can be stored in the Employee
model.
一次具体类(软件工程师,Janitor等)被实例化,它将继承其父类的所有属性。
Once a concrete class (Software Engineer, Janitor etc.) is instantiated it will inherit all attributes from it's parent class.
设置并使用 employee_type
您可以区分下注
Setting and using the employee_type
you can differentiate between which concrete class was created.
有关此内容的更多信息,请参见
More information on this can be found here
[更新]
使用Django 信号可以派生具体的类名称,并将其与关联的实例一起存储。
Using a django Signal the concrete class name can be derived and stored with the associated instance.
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)
这可确保每次都存储正确的标识符。
This ensures the correct identifier is stored every time.
现在在您的视图中要选择具体类的类型使用过,您可以在以下情况下继续使用它:
Now in your view where you want to select the type of concrete class to be used, you can either continue using it in a condition like so:
views.py
#EXAMPLE USAGE
employee = Employee.objects.get(id=1)
if employee.employee_type == 'Janitor':
employee = Janitor.objects.get(id=employee.id)
或通过使用模型名称和对全局变量的查找来动态派生它。
Or derive it dynamically by using the model name and a lookup to global variables.
#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
函数将所有旧名称转换为新名称。有关更多信息,请
Note: Be careful when changing names of parent or child models as this will affect historical records using the old model name. To fix this use django's update()
or bulk_update
function to convert all old names to new names. More info is here
选项2- django-polymorphic
OPTION 2 - django-polymorphic
使用名为的django软件包django-polymorphic
,这允许在查询父类时返回所有具体的类。
Using a django package called django-polymorphic
, this allows all concrete classes to be returned when parent class is queried on.
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)
[...]
在<$ c $上查询时c>员工会返回以下模型
>>> Employee.objects.filter(id=1)
[ <Employee: id 1, name "Joe Bloggs", email "[email protected]">,
<SoftwareEngineer: id 1, name "Joe Bloggs", email "[email protected]", predominant_programming_language "Python">,]
这篇关于外键中的许多型号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!