Right way to return proxy model instance from a base model instance in Django?

Viewed 9381

Say I have models:

class Animal(models.Model):
    type = models.CharField(max_length=255)

class Dog(Animal):
    def make_sound(self):
        print "Woof!"
    class Meta:
        proxy = True

class Cat(Animal):
    def make_sound(self):
        print "Meow!"
    class Meta:
        proxy = True

Let's say I want to do:

 animals = Animal.objects.all()
 for animal in animals:
     animal.make_sound()

I want to get back a series of Woofs and Meows. Clearly, I could just define a make_sound in the original model that forks based on animal_type, but then every time I add a new animal type (imagine they're in different apps), I'd have to go in and edit that make_sound function. I'd rather just define proxy models and have them define the behavior themselves. From what I can tell, there's no way of returning mixed Cat or Dog instances, but I figured maybe I could define a "get_proxy_model" method on the main class that returns a cat or a dog model.

Surely you could do this, and pass something like the primary key and then just do Cat.objects.get(pk = passed_in_primary_key). But that'd mean doing an extra query for data you already have which seems redundant. Is there any way to turn an animal into a cat or a dog instance in an efficient way? What's the right way to do what I want to achieve?

5 Answers

I played around with a lot of ways to do this. In the end the most simple seems to be the way forward. Override __init__ of the base class.

class Animal(models.Model):
    type = models.CharField(max_length=255)
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__class__ = eval(self.type)

I know eval can be dangerous, bla bla bla, but you can always add safeguarding/validation on the type choice to ensure it's what you want to see. Besdies that, I can't think of any obvious pitfalls but if i find any i'll mention them/ delete the answer! (yeah i know the question is super old, but hopefully this'll help others with the same problem)

You can perhaps make Django models polymorphic using the approach described here. That code is in early stages of development, I believe, but worth investigating.

Related