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.
精彩评论