'Django newbie, struggling to understand how to implement a custom queryset

So I'm pretty new to Django, I started playing yesterday and have been playing with the standard polls tutorial.

Context

I'd like to be able to filter the active questions based on the results of a custom method (in this case it is the Question.is_open() method (fig1 below).

The problem as I understand it

When I try and access only the active questions using a filter like questions.objects.filter(is_open=true) it fails. If I understand correctly this relies on a queryset exposed via a model manager which can only filter based on records within the sql database.

My questions

1) Am I approaching this problem in most pythonic/django/dry way ? Should I be exposing these methods by subclassing the models.Manager and generating a custom queryset ? (that appears to be the consensus online).

2) If I should be using a manager subclass with a custom queryset, i'm not sure what the code would look like. For example, should I be using sql via a cursor.execute (as per the documentation here, which seems very low level) ? Or is there a better, higher level way of achieving this in django itself ?

I'd appreciate any insights into how to approach this.

Thanks

Matt

My models.py

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published',default=timezone.now())
    start_date = models.DateTimeField('poll start date',default=timezone.now())
    closed_date = models.DateTimeField('poll close date', default=timezone.now() + datetime.timedelta(days=1))


    def time_now(self):
        return timezone.now()

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

    def is_open(self):
        return ((timezone.now() > self.start_date) and (timezone.now() < self.closed_date))

    def was_opened_recently(self):
        return self.start_date >= timezone.now() - datetime.timedelta(days=1) and self.is_open()

    def was_closed_recently(self):
        return self.closed_date >= timezone.now() - datetime.timedelta(days=1) and not self.is_open()

    def is_opening_soon(self):
        return self.start_date <= timezone.now() - datetime.timedelta(days=1)

    def closing_soon(self):
        return self.closed_date <= timezone.now() - datetime.timedelta(days=1)

[Update]

Just as a follow-up. I've subclassed the default manager with a hardcoded SQL string (just for testing), however, it fails as it's not an attribute

class QuestionManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset()

    def get_expired(self):
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("""
                      select id, question_text, closed_date, start_date, pub_date from polls_question
                      where ( polls_question.start_date < '2017-12-24 00:08') and (polls_question.closed_date > '2017-12-25 00:01') 
                      order by pub_date;""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], closed_date=row[2], start_date=row[3], pub_date=row[4])
                result_list.append(p)
        return result_list

I'm calling the method with active_poll_list = Question.objects.get_expired()

but I get the exception

Exception Value:    
'Manager' object has no attribute 'get_expired'

I'm really not sure I understand why this doesn't work. It must be my misunderstanding of how I should invoke a method that returns a queryset from the manager.

Any suggestions would be much appreciated.

Thanks



Solution 1:[1]

Looks like i'd missed off brackets when defining Question.objects

It's still not working, but I think I can figure it out from here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 MattY