Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django, can I get reference objects included with a queryset

Say I have these models:

models.py:

class Item(models.Model):
    ref_id = models.PositiveIntegerField()
    name = models.CharacterField(max_length=32)

class ItemDue(models.Model):
    item = models.ForeignKey(Item)
    due_date = models.DateField(null=True, blank=True)
    lots of other fields below
    .
    .
    .

I want to query the ItemDue objects but want to also include the Item with the query.

If I get a set of ItemDues I can loop like this:

for item_due in ItemDue.objects.filter(some_criteria):
    print item_due.item.ref_id

However, when I did some performance tests, this is going back to the database to get the referenced Item object, so I have to run another query for each ItemDue to get the Item.ref_id. This makes a difference in a huge query, so I want to get the Item.ref_id along with the queryset to get the ItemDues. I can do .values('id', 'item__ref_id') to get a dictionary of the ItemDue with the id and item__ref_id. So, I could use .values('id', 'item__ref_id', ...) for all fields in ItemDue but that would be a lot of work. Is there a simple way I can append to the values of a queryset to get that reference object, without spelling out ALL the fields in the ItemDue along with just the one extra field item__ref_id?

Thanks

EDIT:

Here is some code run in the manage.py shell:

def check():
    start = datetime.now()
    print "Starting {0}".format(datetime.now() - start)
    index = 0
    item_rows = dict()
    print "Getting Items for PG and Parents {0}".format(datetime.now() - start)

    # items due for PG
    items = pg.item_due.all().filter(disabled=False).select_related()

    # Loop the parents, and chain their items due to the PG items due.
    for p in parents:
        items = itertools.chain(items, p.item_due.all().filter(disabled=False).select_related())
        index += 1
    print "All Items Retrieved {0}".format(datetime.now() - start)
    for item in items:
        pass
    print "Loop Items Complete {0}".format(datetime.now() - start)
    return item_rows

>>> rows = check()
Starting 0:00:00.000008
Getting Items for PG and Parents 0:00:00.000032
All Items Retrieved 0:00:00.004669
Loop Items Complete 0:00:00.022597

Notice the time it takes to loop the items and just pass is about .018 seconds.

Now I simply change the pass in the loop to item.item.ref_id and it takes a LOT longer.

def check():
    start = datetime.now()
    print "Starting {0}".format(datetime.now() - start)
    index = 0
    item_rows = dict()
    print "Getting Items for PG and Parents {0}".format(datetime.now() - start)

    # items due for PG
    items = pg.item_due.all().filter(disabled=False).select_related()

    # Loop the parents, and chain their items due to the PG items due.
    for p in parents:
        items = itertools.chain(items, p.item_due.all().filter(disabled=False).select_related())
        index += 1
    print "All Items Retrieved {0}".format(datetime.now() - start)
    for item in items:
        item.item.ref_id
    print "Loop Items Complete {0}".format(datetime.now() - start)
    return item_rows

>>> rows = check()
Starting 0:00:00.000007
Getting Items for PG and Parents 0:00:00.000031
All Items Retrieved 0:00:00.004712
Loop Items Complete 0:00:00.258209

From .018 seconds to run the loop to .25 seconds. Why does it take 13 times the time just to process the item.item.ref_id if it is getting it from the query already?

like image 272
Furbeenator Avatar asked Dec 19 '13 21:12

Furbeenator


People also ask

Which can be used to retrieve an object directly instead of a QuerySet?

Retrieving Single Objects from QuerySets We can do this using the get() method. The get() returns the single object directly. Let's see the following example. As we can see in both examples, we get the single object not a queryset of a single object.

What is the use of QuerySet in Django?

A QuerySet is a collection of data from a database. A QuerySet is built up as a list of objects. QuerySets makes it easier to get the data you actually need, by allowing you to filter and order the data.

What is annotate in Django QuerySet?

annotate() that has been computed over the objects that are related to the objects in the QuerySet . Each argument to annotate() is an annotation that will be added to each object in the QuerySet that is returned. The aggregation functions that are provided by Django are described in Aggregation Functions below.


1 Answers

Use select_related to get related tables data in one query:

for item_due in ItemDue.objects.filter(some_criteria).select_related():
    print item_due.item.ref_id
like image 123
ndpu Avatar answered Oct 20 '22 10:10

ndpu