开发者

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

开发者 https://www.devze.com 2023-01-20 05:22 出处:网络
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 Supe

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.. Wh开发者_开发百科at 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?


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消