Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using django, how do i construct a proxy object instance from a superclass object instance?

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?

like image 462
Bubba Raskin Avatar asked Oct 13 '10 05:10

Bubba Raskin


People also ask

What is a proxy model Django?

The main Usage of a proxy model is to override the main functionality of existing Model. It is a type of model inheritance without creating a new table in Database. It always query on original model with overridden methods or managers.

What does Django DB models model clean () do?

clean() This method should be used to provide custom model validation and to modify attributes on your model if desired.

What is proxy model in python?

Proxy is a structural design pattern that provides an object that acts as a substitute for a real service object used by a client. A proxy receives client requests, does some work (access control, caching, etc.) and then passes the request to a service object.


2 Answers

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
like image 57
Manoj Govindan Avatar answered Sep 19 '22 01:09

Manoj Govindan


I would do:

def reklass_model(model_instance, model_subklass):

    fields = model_instance._meta.get_all_field_names()
    kwargs = {}
    for field_name in fields:
        try:
           kwargs[field_name] = getattr(model_instance, field_name)
        except ValueError as e: 
           #needed for ManyToManyField for not already saved instances
           pass

    return model_subklass(**kwargs)

animals = Animal.objects.all()
for animal in animals:
   if (animal.type == "cat"):
      animal_proxy = reklass_model(animal, Cat)
   elif (animal.type == "dog"):
      animal_proxy = reklass_model(animal, Cat)
   animal_proxy.make_noise()

# Meow Meow
# Woof Woof

I have not tested it with "the zoo" ;) but with my own models seems to work.

like image 32
fero Avatar answered Sep 18 '22 01:09

fero