Many models in Foreign Key

℡╲_俬逩灬. 提交于 2020-08-24 22:30:06

问题


Let's say I have a model, employee:

Employee

  • Name: CharField
  • Email: EmailField
  • Type: ForeignKey

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.

I could do this by having a foreign key field for all types, and setting many to null, but this seems bad. Does this make sense?

How can I achieve this? Thanks!!


回答1:


There are many ways you can model this level of polymorphism into a scenario like this.

OPTION 1 - Concrete Base Model

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):
    [...]

All mutual attributes can be stored in the Employee model.

Once a concrete class (Software Engineer, Janitor etc.) is instantiated it will inherit all attributes from it's parent class.

Setting and using the employee_type you can differentiate between which concrete class was created.

More information on this can be found here

[Update]

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)
[...]

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

OPTION 2 - 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)
    [...]

When querying on the Employee models the following will be returned

>>> 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">,]


来源:https://stackoverflow.com/questions/63019628/many-models-in-foreign-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!