确定Django中外键的类别

确定Django中外键的类别,django,Django,我有以下型号: class Vehicule(models.Model): brand = models.CharField() description = models.TextField() color = models.CharField() class Car(Vehicule): wheels = models.IntegerField(editable=False, default=4) class Boat(Vehi

我有以下型号:

class Vehicule(models.Model):
    brand        = models.CharField()
    description  = models.TextField()
    color        = models.CharField()

class Car(Vehicule):
    wheels = models.IntegerField(editable=False, default=4)

class Boat(Vehicule):
    length = models.IntegerField()

class Suprise(models.Model):
    items = models.ForeignKey(Vehicule)
车辆
可以是抽象的

如果我创建了
Car
的实例,我可以访问
Car
的所有属性,也可以访问
vehicleue
的属性。这就是继承的全部意义

但是,当我在
惊喜
的实例中尝试确定
项的类别时,出现了一个问题。即,如果
s
是一个
惊喜
,则以下代码:

for i in s.items.all():
    if isinstance(i, Car):
        print("It's a car !'")
    else :
        print("It's not a car !'")
将始终输出“它不是一辆车!”即使这实际上是一辆车。我发现确定
项目类型的唯一方法是检查它们是否具有指向外部表中对象的
car
属性(有关更多详细信息,请参阅官方文档),但这对我来说不够干净


这是确定Django中外键类别的唯一方法吗?

根据Django文档:

如果您有一个同时也是餐厅的地方,您可以使用模型名称的小写版本从地点对象到餐厅对象:

p = Place.objects.get(id=12)
p.restaurant
除此之外:

但是,如果上面示例中的p不是餐厅(它是直接作为Place对象创建的,或者是其他类的父对象),引用p.Restaurant将引发Restaurant.DoesNotExist异常。


因此,您自己回答了这个问题,您需要检查car attr,因为这是指向您要查找的模型的内容,如果没有car attr,则对象不是由car类创建的。

事实是,当您查询模型时(通过QuerySet方法,或间接通过ForeignKey)您得到的是非多态实例——与SQLAlchemy不同,SQLAlchemy得到的是多态实例

这是因为获取的数据只对应于您正在访问的数据(因为它们是预先知道的祖先数据)。默认情况下,Django不会执行任何类型的
select_related
来获取子项,因此您只能使用外键或查询集的基本(即当前)类模型

这意味着:

Vehicle.objects.get(pk=1).__class__ == Vehicle
将永远是真实的,并且:

Surprise.objects.get(pk=1).items.all()[0].__class__ == Vehicle
也永远是真的

假设这些示例中存在pk=1的车辆,存在pk=1的惊喜,并且至少有一个项目)

除了了解你的孩子的课程之外,没有干净的解决办法。正如您所说:访问.car或.truck之类的变量(考虑到存在car和truck类)是一种方法但是如果您点击了错误的子类(例如,当
车辆
实际上应该是
卡车
实例时,您点击了
车辆.汽车
),您将得到一个
ObjectDoesNotExist
错误免责声明:不知道如果在不同模块中有两个同名的子类,会发生什么情况


如果您想拥有多态性行为,它可以将您从测试每个可能的子类中抽象出来,那么存在一个应用程序(实际上没有使用它):

作为另一种解决方法,我编写了这个函数,它可以用于相同的目的,而不需要
django多态性

def is_model_instance(object, model):
    '''
    `object` is expected to be a subclass of models.Model
    `model`  should be a string containing the name of a models.Model subclass

    Return true if `object` has a reference to a `model` instance, false otherwise.
    '''
    model_name = model.lower()

    if model_name in dir(object):
        try:
            statement = "object." + model_name
            exec(statement)
            return True
        except object.DoesNotExist:
            return False
    else :
        return False
然后我可以很容易地做一些像

>>> is_model_instance(Surprise.objects.get(pk=1).items.all()[0], Car)
True     # this is indeed a Car

谢谢,这个
django多态性
似乎正是我所需要的。