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?
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.
clean() This method should be used to provide custom model validation and to modify attributes on your model if desired.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With