1

Basically I have a Base class called "Program". I then have more specific program model types that use Program as a base class. For 99% of my needs, I don't care whether or not a Program is one of the specific child types. Of course there's that 1% of the time that I do want to know if it's one of the children.

The problem is that if I have let's say, a SwimProgram model and a CampProgram model using Program as their base, that it's problematic to find out what they are without a bunch of try/except blocks. What I want is something like the following:

program = models.Program.objects.get(id=15)
if program.swimprogram:
    ## do stuff
elif program.campprogram:
    ## do stuff
else:
    ## do other stuff

Of course this throws DoesNotExist exceptions. I could either use try/excepts which are uglier, or I could have Program have a 'type' field that the children set on save. Both are doable, but I'm curious if anyone has any better methods.

f4nt
  • 2,661
  • 5
  • 31
  • 35

3 Answers3

4

Have you tried hasattr()? Something like this:

if hasattr(program, 'swimprogram'):
    # ...
elif hasattr(program, 'campprogram'):
    # ...

If you are unsure about this approach, try it out in a simple test app first. Here are two simple models that should show if it will work for you and the version of django that you are using (tested in django-1.1.1).

class Archive(models.Model):
    pub_date = models.DateField()

    def __unicode__(self):
        return "Archive: %s" % self.pub_date

class ArchiveB(Archive):
    def __unicode__(self):
        return "ArchiveB: %s" % self.pub_date

And then giving it a spin in the shell:

> a_id = Archive.objects.create(pub_date="2010-10-10").id
> b_id = ArchiveB.objects.create(pub_date="2011-11-11").id
> a = Archive.objects.get(id=a_id)
> b = Archive.objects.get(id=b_id)
> (a, b) # they both look like archive objects
(<Archive: Archive: 2010-10-10>, <Archive: Archive: 2011-11-11>)
> hasattr(a, 'archiveb')
False
> hasattr(b, 'archiveb') # but only one has access to an ArchiveB
True
istruble
  • 13,363
  • 2
  • 47
  • 52
  • Are you sure that works? I'm pretty sure that `program` has both attributes. The DoesNotExist exception only occurs when trying to access them (since inheritance is modeled with a OneToOne relationship) – Benjamin Wohlwend Feb 04 '10 at 21:44
  • yw f4nt. I did pretty much the same thing when i figured it out for myself. @piquadrat, I added an little example to show how it can work. Thanks for the nudge to add the example. – istruble Feb 05 '10 at 00:52
1

A couple of weeks ago, someone on the django-developers mailing list introduced a very interesting extension to Django's ORM that makes QuerySets return subclassed objects instead of objects of the parent class. You can read all about it here:

http://bserve.webhop.org/wiki/django_polymorphic

I haven't tried it myself yet (but certainly will), but it seems to fit your use case.

Benjamin Wohlwend
  • 30,958
  • 11
  • 90
  • 100
  • The link is dead, I think the corresponding project is https://github.com/bconstantin/django_polymorphic but it looks like it never moved since its initial commit... – Stefano Jan 04 '13 at 16:33
0

// Update

As pointed out in the comments to this post I got the question wrong. The answer below will not solve the problem.


Hi f4nt,

the easiest way I can think of right now would be the following:

program = models.Program.objects.get(id=15)

if program.__class__.__name__ == 'ModelA':
  # to something

if program.__class__.__name__ == 'ModelB':
  # to something

To make this a bit better you could write a method in the base model:

class MyModel(models.Model):

  def instanceOfModel(self, model_name):
    return self.__class__.__name__ == model_name

That way the code from above would look like this:

program = models.Program.objects.get(id=15)

if program.instanceOfModel('ModelA'):
  # to something

if program.instanceOfModel('ModelB'):
  # to something

But as you can imagine this is ugly. You could look into the content type framework which might help you to do the same except more elegent.

Hope that helps!

Jens
  • 20,533
  • 11
  • 60
  • 86
  • 1
    I don't hink this will work. Django docu clearly says: *QUERYSETS STILL RETURN THE MODEL THAT WAS REQUESTED* . So if you request Program objects then `program.__class__.__name__` will be `Program`. – Felix Kling Feb 04 '10 at 19:34
  • Thank you for pointing that out Felix. But I tried it in the django shell and it worked. – Jens Feb 04 '10 at 19:39
  • Yeah, just did a quick test, and it will return "Program". – f4nt Feb 04 '10 at 19:39
  • I think I got want you want to say. You are only fetching the Program objects (not SwimProgram etc.) and now want to know what it really is. Sorry. – Jens Feb 04 '10 at 19:46
  • No worries :) I'm just going to have the children set a Type field on save instead. I don't necessarily like it, but it'll work until I think of something better. Try/Except would work if I didn't plan on subclassing Program a few more times, just afraid the code'll become a rat nest with try/except. – f4nt Feb 04 '10 at 19:51