views:

18

answers:

1

I am still a bit confused about the relation of Proxy models to their Superclasses in django. My question now is how do I get a instance of a Proxy model from an already retrieved instance of the Superclass?

So, lets say I have:

class Animal(models.Model):
   type = models.CharField(max_length=20)
   name = models.CharField(max_length=40)

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

   def make_noise(self):  
       print "Woof Woof"  

Class Cat(Animal):  
   class Meta:
       proxy = True

   def make_noise(self):  
       print "Meow Meow"

animals = Animal.objects.all()
for animal in animals:
   if (animal.type == "cat"):
      animal_proxy = # make me a cat
   elif (animal.type == "dog"):
      animal_proxy = # make me a dog
   animal_proxy.make_noise()

OK. So.. What goes into "# make me a cat" that doesn't require a query back to the database such as:

animal_proxy = Cat.objects.get(id=animal.id)

Is there a simple way to create an instance of Cat from an instance of Animal that I know is a cat?

+1  A: 

You are trying to implement persistence for an inheritance hierarchy. Using one concrete table and a type switch is a nice way to do this. However I think that your implementation, specifically:

for animal in animals:
   if (animal.type == "cat"): 
      animal_proxy = # make me a cat

is going against the grain of Django. The switching on type shouldn't be extraneous to the proxy (or model) class.

If I were you, I'd do the following:

First, add a "type aware" manager to the proxy models. This will ensure that Dog.objects will always fetch Animal instances with type="dog" and Cat.objects will fetch Animal instances with type="cat".

class TypeAwareManager(models.Manager):
    def __init__(self, type, *args, **kwargs):
        super(TypeAwareManager, self).__init__(*args, **kwargs)
        self.type = type

    def get_query_set(self):
        return super(TypeAwareManager, self).get_query_set().filter(
              type = self.type)

class Dog(Animal):
    objects = TypeAwareManager('dog')
    ...

class Cat(Animal):
    objects = TypeAwareManager('cat')
    ...

Second, fetch subclass instances separately. You can then combine them before operating on them. I've used itertools.chain to combine two Querysets.

from itertools import chain
q1 = Cat.objects.all() # [<Cat: Daisy [cat]>]

q2 = Dog.objects.all() # [<Dog: Bruno [dog]>]

for each in chain(q1, q2): 
    each.make_noise() 

# Meow Meow
# Woof Woof
Manoj Govindan
I am aware that I am going against the grain of Django. I do this because Django does not let me do what I want, which is get a list of objects that are stored in the same table but which have different properties without actually chaining together results. I built a type aware manager, but at the superclass level and now I just need to 'cast' returned superclass object instances to be the proxy class objects. Is there any way to do this?
Bubba Raskin
Actually, I have already done this, but I am currently doing a call back to the database as per: animal_proxy = Cat.objects.get(id=animal.id) I want something like animal_proxy = (Cat) animal . i know there has to be python trickery that can do this for me.
Bubba Raskin
@Bubba: See this question. Answers might be of interest to you. http://stackoverflow.com/questions/2218867/right-way-to-return-proxy-model-instance-from-a-base-model-instance-in-django
Manoj Govindan
I saw that question. I based my example off that question.
Bubba Raskin
I am already getting back mixed Cat+Dog results by hacking queryset on the Animal class. I just want to avoid making the calls to the database when instantiating Cat+Dog from an Animal instance returned by the queryset.
Bubba Raskin