4

I have these models:

class Foo(models.Model):
    some_field = models.CharField()
    class Meta:
        pass

class Bar(Foo):
    some_other_field = models.CharField()
    class Meta:
        pass

The example is simplified, in reality both models have a lot of fields.

When I query Bar, the Django ORM creates a query containing an inner join with Foo.
I don't need the information in Foo.

Question: Is there a way to query Bar without an inner join with Foo?

I realize that removing Bar extending Foo and making it a foreign key would be a better way to solve this problem. However, there's a lot of legacy code relying on this so I'd prefer a quick solution until I have the time and guts to refactor legacy parts of the app.

I also realize I can write an SQL query myself, but I'd prefer a solution that uses the ORM.

3 Answers 3

3

The way I've done it is to use a new unmanaged model for this instance

class SkinnyBar(models.Model):
    some_other_field = models.CharField()

    class Meta:
        managed = False
        db_table = "app_bar"

This will allow you to use the ORM.

If you want to avoid the duplication you could try adding most of your properties and methods to a meta class

class BaseBar(models.Model):
    some_other_field = models.CharField()

    def some_common_method(self):
        return True

    class Meta:
        abstract = True


class Bar(BaseBar, Foo):

    def some_method_that_requires_foo(self):
        return self.some_field == 1


class SkinnyBar(BaseBar):

    class Meta:
        managed = False
        db_table = "app_bar"
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your answer. I think this could work for me, but I still have an issue: I have a lot of fields and methods in my actual Bar model object, is there a way to add/mix these in SkinnyBar without duplication?
0

The proper way of doing this is making the Foo model abstract by adding abstract = True in the Meta. In your case, some_field is in the app_foo table and the join is needed in order to create the Bar object completely. If you don't need some_field, you can always drop to raw SQL. But in order to retrieve it, you must join app_bar and app_foo tables.

2 Comments

To clarify my question a bit: I don't need some_field in this case, but I do need to keep it working in all other (legacy) cases. Migrating both tables to one table is not something I want to do as the dataset is too big. — Are you sure there isn't a solution using the ORM?
Apart from raw query, no. The join is explicit. The object needs it to be complete.
0

Interesting issue you have.

The inner join on it's own shouldn't be that big of a deal so long as your queries are setup appropriately. What is the main reason for trying to limit it, performance I assume? Can you post some code of how you're trying to work on these models?

I'm not positive this would work, but you could always look into using the 'defer' function on your queryset. This function is really only for advanced use-cases, and may not apply here. In essence, defer doesn't try to query the fields you specify. if you defer'some_field', perhaps it won't do the join. The downside is that as soon as you try to access 'some_field' from the object, it will perform a query (iteration will cause n extra queries).

1 Comment

1. Yes it is for performance. 2. Mainly using Bar.objects.filter() and Bar.objects.get(). 3. I have tried both defer and only, but for example Bar.objects.all().only('some_other_field') does not get rid of the inner join.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.