Django model manager queryset filter on aware datetime: no item display until server restart

1k views Asked by At

I have a Django model which has a publication_date and an is_published fields. I've created a manager for this model which returns all published items which means: every item that has is_published=True and publication_date <= now.

class PublishedTextManager(models.Manager):
    """
        Filters out all unpublished items and items with a publication date in the future
    """
    def get_query_set(self):
        return super(PublishedTextManager, self).get_query_set() \
            .filter(is_published=True) \
            .filter(publication_date__lte=timezone.now())

The view that's using this manager looks like this:

class NewsAndEventsOverView(ListView):

    model = News
    queryset = News.published.all().order_by('-publication_date')
    context_object_name = 'news_list'

    def get_context_data(self, **kwargs):
        # Initialize context and fill it with default data from NewsAndEventsOverView super class
        context = super(NewsAndEventsOverView, self).get_context_data(**kwargs)
        # Add view specific context
        context['latest_news_item'] = context['news_list'][0]
        today = timezone.now()
        yesterday = today - timedelta(days=1)
        context['upcoming_events_list'] = Event.published.filter(Q(date_end__gt=yesterday) | Q(date_start__gt=yesterday)).order_by('date_start')
        past_events_list = Event.published.filter(Q(date_end__lt=today) | Q(date_start__lt=today)).order_by('-date_start')
        old_news_list = context['news_list'][1:]
        context['old_news_and_events_list'] = sorted(chain(old_news_list, past_events_list), key=lambda x: x.publication_date, reverse=True)
        return context

Relevant urls.py:

from .views import NewsAndEventsOverView

urlpatterns = patterns('',
    # Index page
    url(r'^$', NewsAndEventsOverView.as_view(), name="newsandevents_overview"),
)

When I add a news item by default it receives the current datetime (timezone.now()) as publication date, however when I refresh the page it doesn't display in the front-end until I do a server restart (using django built-in server a.t.m). I am in Amsterdam time (+2:00) and when I add 2 hours to the publication_date filter it works fine, so since I'm new to datetime awareness I'm guessing I'm doing something wrong. I've tried the timezone.now with and without brackets, but that doesn't make a difference.

2

There are 2 answers

1
Vinod Kurup On BEST ANSWER

I've been running into a similar issue, and here's what I think is going on. When you use the queryset class attribute, the query gets run on each request, but the timezone.now() call within the Manager does not get run on each request, only at class instantiation. Try using the get_queryset method instead, which forces it to be run on each request:

class NewsAndEventsOverView(ListView):

    model = News
    context_object_name = 'news_list'

    def get_queryset(self):
        return News.published.all().order_by('-publication_date')

    ...
1
Simanas On

I am 99% sure that you have something like that on your models:

class News(models.Model):
    ....
    publication_date = models.DateTimeField(default=timezone.now())
    ....

What this actually does it gives a value to keyword argument 'default' instead of passing a function as an object, to be called every time you create a new object. And you want to pass a function 'timezone.now' but not a value of this function, wich would be 'timezone.now()'

Change it to this:

class News(models.Model):
    ....
    publication_date = models.DateTimeField(default=timezone.now)
    ....

And do not forget to apply this everywhere on your code where you want to give default timezone.now value to DateTimeField

Cheets! Do not forget to up-vote if you find this answer useful! ;)