2

I'm creating a website that allows for the creation of events, and those events will be shown on both the home page, and on the events list, so that people can click on the events on view the descriptions of them, as well as check in at the event. I'm having an issue getting a query set within a class to update when a new event is added.

So this function is for our main home page. The query_set updates every time you go back to the page, so when a new event is added, it is shown on this page.

def index(request):
    num_future_events = Event.objects.filter(date__gte=today).filter(approved=True).count()
    query_set = Event.objects.filter(date__gte=today).filter(approved=True).order_by('date', 'time')[:3]

    context = {
        'num_future_events': num_future_events,
        'query_set': query_set
    }

    return render(request, 'index.html', context=context)

This is a class for the events list page on my site. It uses a query set but since the class is only ever called once, the query set will only update with the class's initialization.

class EventListView(generic.ListView):
    model = Event
    query_set = Event.objects.filter(date__gte=today).exclude(approved=False).order_by('date', 'time')[:21]
    template_name = 'listPage.html'

    def get_queryset(self):
        return self.query_set

I was hoping to get some help with making it so when a new event is added, the query set in the EventListView class updates automatically so that it will be displayed on this page.

Any help will be greatly appreciated.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Sam
  • 47
  • 5

1 Answers1

5

If we take a look at Django's source code for a ListView, we see:

def get_queryset(self):
    """
    Return the list of items for this view.
    The return value must be an iterable and may be an instance of
    `QuerySet` in which case `QuerySet` specific behavior will be enabled.
    """
    if self.queryset is not None:
        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            queryset = queryset.all()
    elif self.model is not None:
        queryset = self.model._default_manager.all()
    else:
        raise ImproperlyConfigured(
            "%(cls)s is missing a QuerySet. Define "
            "%(cls)s.model, %(cls)s.queryset, or override "
            "%(cls)s.get_queryset()." % {
                'cls': self.__class__.__name__
            }
        )
    ordering = self.get_ordering()
    if ordering:
        if isinstance(ordering, str):
            ordering = (ordering,)
        queryset = queryset.order_by(*ordering)
    return queryset

Django thus each time construct a new queryset, by calling .all() it makes a unevaluated clone of the queryset, such that this is forced to get reevaluated.

In this case, I thus propose that you do not overwrite get_queryset, but simply use the queryset attribute, like:

class EventListView(generic.ListView):
    model = Event
    queryset = Event.objects.filter(
        date__gte=today
    ).exclude(
        approved=False
    ).order_by('date', 'time')[:21]
    template_name = 'listPage.html'

Or if you really want to override the get_queryset function you can use:

class EventListView(generic.ListView):
    model = Event
    template_name = 'listPage.html'

    def get_queryset(self):
        return Event.objects.filter(
            date__gte=today
        ).exclude(approved=False).order_by('date', 'time')[:21]

or:

class EventListView(generic.ListView):
    model = Event
    template_name = 'listPage.html'
    query_set = Event.objects.filter(
        date__gte=today
    ).exclude(approved=False).order_by('date', 'time')[:21]

    def get_queryset(self):
        return self.query_set.all()
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555