Either
A) Use multiple table inheritance and create a "Eater" base class, that Cat, Cow and Human inherit from.
B) Use a Generic Relation, where Food could be linked to any other Model.
Those are well-documented and officially supported features, you'd better stick to them to keep your own code clean, avoid workarounds and be sure it'll be still supported in the future.
-- EDIT ( A.k.a. "how to be a reputation whore" )
So, here is a recipe for that particular case.
Let's assume you absolutely want separate models for Cat, Cow and Human. In a real-world application, you want to ask to yourself why a "category" field wouldn't do the job.
It's easier to get to the "real" class through generic relations, so here is the implementation for B. We can't have that 'food' field in Person, Cat or Cow, or we'll run into the same problems. So we'll create an intermediary "FoodConsumer" model. We'll have to write additional validation tests if we don't want more than one food for an instance.
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Food(models.Model):
"""Food, by name."""
name = models.CharField(max_length=25)
# ConsumedFood has a foreign key to Food, and a "eaten_by" generic relation
class ConsumedFood(models.Model):
food = models.ForeignKey(Food, related_name="eaters")
content_type = models.ForeignKey(ContentType, null=True)
object_id = models.PositiveIntegerField(null=True)
eaten_by = generic.GenericForeignKey('content_type', 'object_id')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
address = models.CharField(max_length=100)
city = models.CharField(max_length=50)
foods = generic.GenericRelation(ConsumedFood)
class Cat(models.Model):
name = models.CharField(max_length=50)
foods = generic.GenericRelation(ConsumedFood)
class Cow(models.Model):
farmer = models.ForeignKey(Person)
foods = generic.GenericRelation(ConsumedFood)
Now, to demonstrate it let's just write this working doctest:
"""
>>> from models import *
Create some food records
>>> weed = Food(name="weed")
>>> weed.save()
>>> burger = Food(name="burger")
>>> burger.save()
>>> pet_food = Food(name="Pet food")
>>> pet_food.save()
John the farmer likes burgers
>>> john = Person(first_name="John", last_name="Farmer", birth_date="1960-10-12")
>>> john.save()
>>> john.foods.create(food=burger)
<ConsumedFood: ConsumedFood object>
Wilma the cow eats weed
>>> wilma = Cow(farmer=john)
>>> wilma.save()
>>> wilma.foods.create(food=weed)
<ConsumedFood: ConsumedFood object>
Felix the cat likes pet food
>>> felix = Cat(name="felix")
>>> felix.save()
>>> pet_food.eaters.create(eaten_by=felix)
<ConsumedFood: ConsumedFood object>
What food john likes again ?
>>> john.foods.all()[0].food.name
u'burger'
Who's getting pet food ?
>>> living_thing = pet_food.eaters.all()[0].eaten_by
>>> isinstance(living_thing,Cow)
False
>>> isinstance(living_thing,Cat)
True
John's farm is in fire ! He looses his cow.
>>> wilma.delete()
John is a lot poorer right now
>>> john.foods.clear()
>>> john.foods.create(food=pet_food)
<ConsumedFood: ConsumedFood object>
Who's eating pet food now ?
>>> for consumed_food in pet_food.eaters.all():
... consumed_food.eaten_by
<Cat: Cat object>
<Person: Person object>
Get the second pet food eater
>>> living_thing = pet_food.eaters.all()[1].eaten_by
Try to find if it's a person and reveal his name
>>> if isinstance(living_thing,Person): living_thing.first_name
u'John'
"""