hughes
hughes

Reputation: 5713

How can I get all of the django foreignkey objects in one shot?

Context: Foo is a class that has a foreignKey field pointing to a Bar object. Several Foos may refer to the same Bar. (Many to one)

I need to reference several fields of a Foo object's foreignkey (Bar.field and Bar.field2), but it seems that each time I do so it runs a separate query.

As a result, referencing these fields can take forever.

Is there a way to get all the Bar objects in a single query? Or is there a better way to speed it up?

foo_list = Foo.objects.filter(params) # takes 0.001 sec, returns 10k objects

result = []

for f in foo_list:

    # this part takes 40 seconds due to the huge number of queries 
    bar = f.bar # Foo contains a foreignKey reference to the Bar class

    result.append({
        "field": f.field,
        "barField": bar.barField,
        "barField2": bar.barField2,

        # this way was even worse
        # "barField": f.bar.barField,
        # "barField2": f.bar.barField2, 
    })

Upvotes: 0

Views: 185

Answers (3)

Pannu
Pannu

Reputation: 2657

Use relationship look up's eg:

foo_list = Foo.objects.filter(params).values('field','bar__barField','bar__barField2')

This will do the job in one query and the result set will have the required fields as well.

Upvotes: 0

Michael C. O'Connor
Michael C. O'Connor

Reputation: 9890

Yes, you can use the select_related method on your queryset (https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.select_related), which will do the joins for you in the initial query. So in your case, something like:

foo_list = Foo.objects.filter(params).select_related()

If you have a very deep chain of linked models, it may be worth also limiting the depth so that you only get the bar objects (and not any additional links beyond that like .select_related(depth=1)

Upvotes: 1

Lacrymology
Lacrymology

Reputation: 2259

foo_list = Foo.objects.filter(params).select_related()

does the job. The rest of the code would remain the same, but

for f in foo_list:
    f.bar

would use the cached objects

Upvotes: 3

Related Questions