Search code examples
djangodjango-modelsdjango-querysetdjango-managers

Object creation assigns proxy class based on base model field(s)


I'm hoping to be able to create an object using a base model but have that object actually be created as a proxy class depending on the object's field(s).

So for example, for the following models:

class Animal(models.Model):
    species = models.CharField()

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

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

How can I set it up so that

cat = Animal.objects.create(species="cat")
dog = Animal.objects.create(species="dog")

Animal.objects.all() # Returns queryset of [cat, dog]
Cat.objects.all() # Returns queryset of [cat]
Dog.objects.all() # Returns queryset of [dog]

Edit: I would be interested in both types of solutions:

a) The object is created as a Animal first then converted to the proxy class afterwards

b) The object is created directly as the proxy class

(Solution type A is probably most relevant to my use case unfortunately)


Solution

  • Define a custom manager for Animal which will filter the queryset based on the species. Here, we will use the class name as the basis of the species' name.

    class AnimalManager(models.Manager):
        def get_queryset(self):
            if self.model is Animal:
                return super().get_queryset()
            return super().get_queryset().filter(species=self.model.__name__.casefold())
    
    class Animal(models.Model):
        objects = AnimalManager()
        species = models.CharField(max_length=100)
    
    class Cat(Animal):
        class Meta:
            proxy = True
    
    class Dog(Animal):
        class Meta:
            proxy = True
    

    Output

    >>> from my_app.models import *
    >>>
    >>> # Create the animals
    >>> Animal.objects.create(species="cat")
    <Animal: Animal object (1)>
    >>> Animal.objects.create(species="dog")
    <Animal: Animal object (2)>
    >>> Animal.objects.create(species="cat")
    <Animal: Animal object (3)>
    >>> Animal.objects.create(species="cat")
    <Animal: Animal object (4)>
    >>> Animal.objects.create(species="dog")
    <Animal: Animal object (5)>
    >>>
    >>> # Query the animals
    >>> Animal.objects.all()
    <QuerySet [<Animal: Animal object (1)>, <Animal: Animal object (2)>, <Animal: Animal object (3)>, <Animal: Animal object (4)>, <Animal: Animal object (5)>]>
    >>> Cat.objects.all()
    <QuerySet [<Cat: Cat object (1)>, <Cat: Cat object (3)>, <Cat: Cat object (4)>]>
    >>> Dog.objects.all()
    <QuerySet [<Dog: Dog object (2)>, <Dog: Dog object (5)>]>