sapi
sapi

Reputation: 10224

Custom manager - querying on abstract base class

I've written an abstract base class which groups together common fields for some of my tables. I would like to be able to query all of those fields in the same way, using a manager.

class TemporalModel(models.Model):
    objects = models.Manager()
    today = TodayManager()

    time = models.DateTimeField()

    class Meta:
        abstract = True
        get_latest_by = 'time'

One simple example of this, as shown above, is to be able to query subclasses of TemporalModel (which all have a 'time' field) for the entries today.

I thus wrote a manager:

class TodayManager(models.Manager):
    def get_query_set(self):
        now = timezone.now()
        today = datetime(now.year, now.month, now.day, tzinfo=now.tzinfo)
        tomorrow = today + timedelta(days=1)

        qs = models.Manager.get_query_set(self).filter(time >= today)
        qs = qs.filter(time < tomorrow)

        return qs

Attempting to use this fails with a NameError (time not defined). I presume that this is for the same reason as in this question - the field is not on the abstract class, but rather on the subclass.

However, I'm never actually calling this on the abstract class. If I have something like the following:

class SomeTemporalThing(TemporalModel):
    pass # I'm a real table! :)

then I want to be able to call, eg, SomeTemporalThing.today.all(), and have django perform the time >= today / time < tomorrow lookups on the child table.

Is it possible to achieve this without moving the manager definition to the child class (which would result in repetition)?

My reading of the custom manager documentation suggests that it should be possible, but I'm stumped as to how.

Upvotes: 0

Views: 415

Answers (1)

Austin Phillips
Austin Phillips

Reputation: 15756

What you're doing should be possible. The error you are seeing is not related to inheritance of the Manager objects, but rather an incorrect specification to the filter() function of the QuerySet.

Instead of

qs = models.Manager.get_query_set(self).filter(time >= today)

it should be

qs = models.Manager.get_query_set(self).filter(time_gte=today)

The same applies to your time < tomorrow filter parameter.

See QuerySet API documentation.

Upvotes: 2

Related Questions