James Agnew
James Agnew

Reputation: 375

Overriding get_queryset leads to caching headache in ListView, where data remains stale

Could someone explain why overriding get_queryset and referencing the queryset via self completely caches the page? I need to wait 5 minutes or more before updates made to the database display.

I'm trying to save a temporary value to the each object and pass it to the template.

I've got everything working fine and dandy in example 3 but don't really understand what I did to make it work, so any insight would be great!

Example 1: Caches for several minutes, but r.css='abc' work ok

class AppointmentListView(ListView):
    qs = Appointment.objects.prefetch_related('client', 'patients')

    def get_queryset(self):
        for r in self.qs:
            r.css = 'abc' #<-passes temp value to template ok
        return self.qs

Example 2: No caching problem but r.css='abc' now does not work

If I don't include a method and just have the queryset called automatically, there is no caching and database updates display immediately, but my temp data does not reach template. class AppointmentListView(ListView):

    queryset = Appointment.objects.prefetch_related('client','patients')

    for r in queryset:
        r.css = 'abc' #<- NOT passed to template

Example 3: No caching problem AND r.css='abc' works fine

Finally if I put everything in the method, it all works fine - temp data reaches the template and there's no caching.

class AppointmentListView(ListView):

    def get_queryset(self):
        qs = Appointment.objects.prefetch_related('client','patients')
        for r in qs:
            r.css = 'abc' #<-passes to template ok

        return qs

Upvotes: 1

Views: 553

Answers (1)

Derek Kwok
Derek Kwok

Reputation: 13058

The behavior you're seeing is how Python evaluates your code. Below is a simplified example that explains what you're seeing.

import random

class Example1(object):
    roll = random.randint(1, 6) # this is evaluated immediately!
    def get_roll(self):
        return self.roll

ex1 = Example1()

# the call below always returns the same number! 
# (until Python re-interprets the class)
ex1.get_roll() 

If you type the code above into a python interpreter, you'll notice that ex1.get_roll() always returns the same number!

Example1.roll is known as a class or static variable. These are evaluated only once when the class is defined.

class Example2(object):
    def get_number(self):
        roll = random.randint(1,6)
        return roll

In Example2, a new random number is generated everytime get_roll method is called.

For the examples listed in your question:

Example 1

qs is a class variable, and thus only gets evaluated once (which is why you see the "caching" behavior). Subsequent calls to get_queryset returns the same qs variable that was initially evaluated.

Example 2

You didn't override get_queryset, which means ListView.get_queryset implementation is used.

Django's ListView.get_queryset copies the queryset before evaluating it - which is why you don't see "caching". However, because the queryset is copied, the effects from your for loop is thrown away.

Example 3

This is generally the correct way to write your code. You should write your methods like this if you don't want to see "caching" behavior.

Upvotes: 4

Related Questions