Go to Google Groups Home    Django users
Re: Feel free to test queryset-refactor branch

scott lewis <sco...@gmail.com>

On 2008-04-17, at 1553, Justin Fagnani wrote:

> Hey Malcolm,

> I've been using qs-rf for a while now with basically no problems.  
> Excellent work.

> There's one thing that may a little odd that I stumbled on while  
> trying to get some primitive polymorphism working:

> The first thing is that there seems to be no way to tell if an  
> instance of a parent class has a child instance without trying the  
> child reference and catching the DoesNotExist exception. For a class  
> with multiple subclasses, this is a cumbersome, so I've been adding  
> a _type field to parent classes that gets set in save() of the  
> subclasses.

> Is there a better way to do this, or is this something that could be  
> included? I know there's no way to determine whether or not a class  
> will be subclassed in the future, so I wouldn't be surprised if the  
> answer is no. But maybe there should be a documented pattern.

> The odd part is what happens with the child reference. parent.child  
> obviously works as expected, and returns either an instance of Child  
> or raises DoesNotExist. But for an instance of Child, .child always  
> returns a reference to itself, so that c.child == c is always True.  
> This makes sense on one hand, because c is also an instance of  
> Parent, but on the other, Child doesn't have a subclass, so  
> should .child be None?

> I haven't actually encountered this in any real life situation,  
> because it's hard to end up with collection in Django where you have  
> a mix of parent and child instances, so maybe it'll never be a  
> problem.

> One additional thing is that in one case, I know which subclasses  
> I'm interested in, and it'd be great to have a way to specify that a  
> queryset should return polymorphic results by specifying the  
> subclasses for the join. Something like:

> Parent.objects.all().select_subclasses('Child1','Child2')

This is a dirty hack, but it came in handy for me...

If you add this method to your parent class:

     def canonical(self):
         attr_name = '%s_ptr' % self._meta.module_name
         children_fields = [r.get_accessor_name() for r in  
self._meta.get_all_related_objects() if r.field.name == attr_name]
         for f in children_fields:
             try:
                 return getattr(self, f).canonical()
             except models.ObjectDoesNotExist:
                 pass
         return self

You can then convert a queryset to a list of child classes:

     child_classes = [c.canonical() for c in Parent.objects.all()]

Basically, canonical() tries to grab a list of descendant classes,  
then cycles through those until it finds one that exists. If it can't  
find an instance of a descendant class, it just hands back the parent  
since that's what you have.  It's also recursive so it will traverse n-
levels of inheritance.

scott.