Django代理模型:父类访问子类方法

时间:2018-10-13 15:45:03

标签: django django-models

我试图围绕代理模型的工作原理。假设我有一个称为Animal的基类,并且我想实现两个子类:Dog和Cow。它们具有相同的数据要求,所以我真的不想创建两个单独的表。所以我正在尝试使用代理模型:

class Animal(models.Model):
    name = models.CharField()
    animal_type = models.CharField(choices=(('Dog','Dog'),('Cow','Cow')))

    def get_sound(self):
        if animal_type == 'Dog':
            return self.dog.get_sound()    #doesn't work
        elif self.animal_type == 'Cow':
            return self.cow.get_sound()    #doesn't work

class Dog(Animal):
    class Meta:
        proxy=True

    def get_sound(self):
        return 'Woof'

class Cow(Animal):
    class Meta:
        proxy=True

    def get_sound(self):
        return 'Moo'

我的问题是,如何从父类访问子类方法?我的名字是self.dog.get_sound()。在多表继承中这是可能的,但在代理模型中不起作用。

>>obj = Animal.objects.create(name='Max', animal_type='Dog')
>>obj.get_sound()
'Woof'    <-- what I'd like it to return

代理模型是错误的方法吗?希望保留一张桌子。

2 个答案:

答案 0 :(得分:0)

是的,那是行不通的。在幕后,Django为每个继承的模型创建了一个OneToOne外键,关于代理模型(docs的情况除外),您是对的。代理模型并非要以这种方式使用,它们的工作原理与原始模型完全相同,有时在原始表的子集(如您的示例),其他方法等中使用。如果您使用代理模型,则不需要根据需要使用原始表。

我为您提供一些建议:

class DogManager(Manager):

    def get_queryset(self):
        queryset = super().get_queryset()
        return queryset.filter(kind='Dog')  # Literal is not good

    def create(self, **kwargs):
        kwargs.update({'kind': 'Dog'})
        return super().create(**kwargs)


class CowManager(Manager):

    def get_queryset(self):
        queryset = super().get_queryset()
        return queryset.filter(kind='Cow')  # Literal is not good

    def create(self, **kwargs):
        kwargs.update({'kind': 'Cow'})
        return super().create(**kwargs)


class Animal(models.Model):
    name = models.CharField()
    # I've changed the name because I don't like variables with 'type' in the name
    kind = models.CharField(choices=(('Dog','Dog'),('Cow','Cow')))


class Dog(Animal):

    objects = DogManager()

    class Meta:
        proxy=True

    def get_sound(self):
        return 'Woof'


class Cow(Animal):

    objects = CowManager()

    class Meta:
        proxy=True

    def get_sound(self):
        return 'Moo'

现在您无需调用Animal类,因为在这两个代理模型proxy managers中有原始数据的子集。

instance = Dog.objects.create(name='Max')
instance.get_sound()  # Woof

Dog.objects.all() # all animals with kind 'Dog'
Cow.objects.all() # all animals with kind 'Cow'

答案 1 :(得分:0)

给你:

class Animal(models.Model):
    class Type(models.TextChoices):
        Dog = ('dog', 'Dog')
        Cow = ('cow', 'Cow')

    name = models.CharField()
    animal_type = models.CharField(choices=Type.choices)

    def get_subclass_instance(self):
        subclasses = {
            self.Type.choices.Dog: Dog,
            self.Type.choices.Cow: Cow,
        }
        instance = subclasses[self.type].objects.get(id=self.id)
        return instance

    def get_sound(self):
        self.get_subclass_instance().get_sound()


class Dog(Animal):
    class Meta:
        proxy=True

    def get_sound(self):
        return 'Woof'

class Cow(Animal):
    class Meta:
        proxy=True

    def get_sound(self):
        return 'Moo'