Skip to content

Inheritance for foreign keys #11

thibault opened this Issue Apr 13, 2012 · 9 comments

4 participants



I cannot find a way to use the wonderful (it saves my life) InheritanceManager through a ForeignKey. Here's some code, it will be clearer:

class Product(Model):
    objects = InheritanceManager()

class Shirt(Product):

class Jacket(Product):

class Box(Model):
    product = ForeignKey('Product')

boxes = Box.objects.all()
for box in boxes:
    box.product # How to get shirts and jackets here?

Is there any way to do this? Cannot find it in the doc. If not, that would be a great feature to add.


carljm commented Apr 13, 2012

Currently I don't think there's any way to do this. I mean, the manager will be used if its the default manager, but select_subclasses will never be called.

The approach that first comes to mind would be a subclass of InheritanceManager that always overrides get() to return the polymorphic subclass; if that's the default manager on Product it should then work.

Closing this issue for now, though if you find a nice way to do this and want to propose it as an addition to model-utils, I'd be fine with a pull request.

@carljm carljm closed this Apr 13, 2012
carljm commented Apr 13, 2012

Never mind, not sure why I closed it, I'll leave it open since we don't have the feature and I'd accept it in principle (not sure what the API should be though).

@carljm carljm reopened this Apr 13, 2012

I followed your advice, and added a get method in my InheritanceQuerySet:

def get(self, *args, **kwargs):
    Performs the query and returns a single object matching the given
    keyword arguments.
    clone = self.filter(*args, **kwargs).select_subclasses()

    # HERE !
    if not hasattr(self, 'subclasses'):
        clone = clone.select_subclasses()

    if self.query.can_filter():
        clone = clone.order_by()
    num = len(clone)
    if num == 1:
        return clone._result_cache[0]
    if not num:
        raise self.model.DoesNotExist("%s matching query does not exist."
                % self.model._meta.object_name)
    raise self.model.MultipleObjectsReturned("get() returned more than one %s -- it returned %s! Lookup parameters were %s"
            % (self.model._meta.object_name, num, kwargs))

It works to a certain extent:

type(box.product) # 'Jacket'

But it won't performs the joins in the initial query. I don't know how we could make it better.

AlexCid commented Jan 10, 2015

Hey ! I was looking for the solution of this problem and found this.I was wondering, did you actually fix this issue ? The proposed feature addition didn't seem to have made it into the master branch...

carljm commented Jan 10, 2015

@AlexCid No, nobody has written any code to implement the proposed feature. If they had and it had been merged, this issue would be closed.

AlexCid commented Jan 10, 2015

too bad :( CrazyCasta's message seemed to imply that the code was available but it seems i misunderstood him. Thanks for your rapid answer anyways !


I'm still beating my head against the ForeignKey field, trying to grok it, but...

Would it be possible to create a ForeignKey subclass that overrides however the hell it fetches its value, so that it goes through an InheritanceManager and calls select_subclasses()?

carljm commented Feb 2, 2016

@AndrewHows I'd think that would be possible, yeah. That's actually not a bad API for this; placing the decision on the FK side makes some sense.


Ok. If I get it working, I'll send a pull request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.