dannyroa
dannyroa

Reputation: 5571

Polymorphism in Django

I have the following models. How do I get access to the unicode of the inheriting tables (Team and Athete) from the Entity table? I'm trying to display a list of all the Entities that displays 'name' if Team and 'firstname' and 'lastname' if Athlete.

class Entity(models.Model):
  entity_type_list = (('T', 'Team'), ('A', 'Athlete'))
  type = models.CharField(max_length=2, choices=entity_type_list,default='P') 
  pictureurl = models.URLField('Picture Url', verify_exists=False, max_length=255, null=True, blank=True)


class Team(Entity):

  name = models.CharField(max_length=100)

  def __unicode__(self):
    return self.name

class Athlete(Entity):

  firstname = models.CharField(max_length=100)
  lastname = models.CharField(max_length=100)

  def __unicode__(self):
    return '%s %s' % (self.firstname, self.lastname)

Upvotes: 2

Views: 3028

Answers (8)

zlovelady
zlovelady

Reputation: 4097

This answer from Carl Meyer to the question mentioned earlier by Paul McMillan might be what your looking for. A subtlety to this problem not captured in some of the answers is how to get at derived class instances from a QuerySet on Entity.

The Problem

for entity in Entity.objects.all()
  print unicode(entity) # Calls the Entity class unicode, which is not what you want.

A Solution

Use the InheritanceCastModel mixin in the answer linked above as a base class for Entity. You can then cast from Entity instances to the actual derived class instances. This is particularly handy when you want to use querysets on your parent class (Entity) but access the derived class instances.

class Entity(InheritanceCastModel):
  # your model definition. You can get rid of the entity_type_list and type, as the
  # real_type provided by InheritanceCastModel provides this info

class Athlete(Entity):
  # unchanged

class Team(Entity):
  # unchanged

for entity in Entity.objects.all():
  actual_entity = entity.cast()
  print unicode(actual_entity) # actual entity is a a Team or Athlete

Upvotes: 6

ieure
ieure

Reputation: 2421

I don't believe you have to do anything. If Entity is never instantiated directly, you will never call the non-existent Entity.__unicode__ method. However, if you'd like to play it save, you could add a stub method in your Entity class:

class Entity(models.Model):

    def __unicode__(self):
        pass

class Team(Entity):

    def __unicode__(self):
        return self.name

class Athlete(Entity):

    def __unicode__(self):
        return '%s %s' % (self.firstname, self.lastname)

You are now assured that any class which inherits from Entity will have a __unicode__ method, and you can simply traverse them:

for thing in [TeamA(), AthleteA(), TeamB(), AthleteB()]:
    print unicode(thing)

Upvotes: 0

Adam Dziendziel
Adam Dziendziel

Reputation: 925

If I undestand correctly, you are simply asking how to call the __unicode__ method of a given object.

Use unicode(instance) and depending on the type of entity, the appropriate implementation will be called polymorphically.

Upvotes: 0

Thierry Lam
Thierry Lam

Reputation: 46264

From pure Python, you can use the isinstance function:

class Entity:
    def __init__(self):
        if isinstance(self, Team):
            print 'is team'
        elif isinstance(self, Athlete):
            print 'is athlete'

class Team(Entity):
    def __unicode__(self):
        return 'Team'

class Athlete(Entity):
    def __unicode__(self):
        return 'Athlete'

Upvotes: 1

David
David

Reputation: 1679

This is a little ugly, but I think it should work:

entities = Entity.objects.all()
for entity in entities:
   try:
       print entity.team
   except:
       print entity.athlete

Check out http://docs.djangoproject.com/en/1.0/topics/db/models/#id7 for more on multi-table inheritance. Just be careful, because the Django ORM is inevitably a leaky abstraction and things you might normally do with objects can get you in trouble, or do unexpected things.

Upvotes: 0

Paul McMillan
Paul McMillan

Reputation: 20107

I answered a similar question a while ago. Have a look, I think one of the answers probably solves your problem as well.

How do I access the child classes of an object in django without knowing the name of the child class?

My answer from there was to add this to the parent class:

def get_children(self):
    rel_objs = self._meta.get_all_related_objects()
    return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]

Then you can call that function to get the children objects (in your case you will only have one) and then call the unicode function from that object.

Upvotes: 0

Mikhail P
Mikhail P

Reputation:

Loop over all the entities... if entity.class == 'Athlete' and entity.firstname and entity.lastname: blah

Hope this helps.

Edit: hmmm looks like I forgot about actually getting the combined list of both entities. Not sure I know of a slick way to do that.

Upvotes: 0

Anurag Uniyal
Anurag Uniyal

Reputation: 88737

I am not very clear what you want to do, but in any case you can add a criteria in dervied class instead of checking unicode method of derived classes

e.g. you can ask the class isTypeA ? or why don't you check the type?

Upvotes: 0

Related Questions