Search code examples
djangodjango-modelsdjango-querysetdjango-managers

How to access custom QuerySet methods from the Manager of a ForeignKey


I'm using Django Managers to make a higher API to interact with my database and keeping my code cleaner and more readable. But in case I have a Foreignkey relationship, I can't use the manager of the ForeignKey model. The queries are more complex as below, but I just simplified the example so, It can be easier to read and get the idea of the question:

models.py:

class Community(models.Model):
    objects = CommunityManager()
    ...


class Inscription(models.Model):
    objects = InscriptionManager()

    ...
    community = models.ForeignKey("Community", related_name="inscriptions")
    created_at = models.DateTimeField()

managers.py:

from datetime import date
from django.db import models


class InscriptionQuerySet(models.query.QuerySet):
    def by_day(self, day=date.today()):
        return self.filter(created_at__day=day)

    ... # more queries


class InscriptionManager(models.Manager):
    def get_query_set(self):
        return InscriptionQuerySet(self.model, using=self._db)

    def today(self):
        return self.get_query_set().by_day()

    ... # more queries


class CommunityQuerySet(models.query.QuerySet):
    def by_type(self, type):
        return self.filter(type=type)

    ... # more queries


class CommunityManager(models.Manager):
    def get_query_set(self):
        return OrganistaionQuerySet(self.model, using=self._db)

    def by_type(self, type):
        return self.get_query_set().by_type(type)

    ... # more queries

Usage:

Inscription.objects.by_day() # return correctly all the inscriptions made today
Community.objects.by_type('type1') # return correctly all Communities that match

Problem: but here lies the problem

community_b = Community.objects.get(id=12)
community_b.inscriptions.by_day() 
>>> AttributeError: 'ForeignRelatedObjectsDescriptor' object has no attribute 'by_day'

How can I fixe this. How to customize the manager to take in consideration the models relation.


Solution

  • I don't see the need of both Manager and QuerySet in your approach. You could just do away with a QuerySet. This is how I generally implement custom managers, and I tried accessing custom manager method on reverse relationship in one of my projects, and it works fine:

    class InscriptionQuerySet(models.QuerySet):
        def by_day(self, day=date.today()):
            return self.filter(created_at__day=day)
    
        def today(self):
            return self.by_day()
    
    class CommunityQuerySet(models.QuerySet):
        def by_type(self, type):
            return self.filter(type=type)
    

    And then in your models, change your objects like this:

    class Community(models.Model):
        objects = CommunityQuerySet.as_manager()
        ...
    
    
    class Inscription(models.Model):
        objects = InscriptionQuerySet.as_manager()
    

    I think you would be able to access custom queryset method with this setup.