Skip to content

Inheritance for foreign keys #11

Open
thibault opened this Issue Apr 13, 2012 · 9 comments

4 participants

@thibault

Hi,

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.

Regards,
Thibault

@carljm
Owner
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
Owner
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
@thibault

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
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
Owner
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
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 !

@AndrewHows

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
Owner
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.

@AndrewHows

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.