Motiejus Jakštys
Motiejus Jakštys

Reputation: 2979

Abstract Django model manager

I lack Python metaprogramming knowledge here. Let's say I have the following:

class OwnCompanyManager(models.Manager):
    """Only companies of this user"""
    def get_queryset(self, user):
        if user.is_superuser:
            return super(OwnCompanyManager, self).get_queryset()
        return super(OwnCompanyManager, self).get_queryset().filter(
            companyuser__user=user)


class OwnPublisherManager(models.Manager):
    """Only publishers of this user's company"""
    def get_queryset(self, user):
        if user.is_superuser:
            return super(OwnPublisherManager, self).get_queryset()
        return super(OwnPublisherManager, self).get_queryset().filter(
            company__companyuser__user=user)

class Company(models.Model):
    name = models.CharField(max_length=45)

    objects = models.Manager()
    own = OwnCompanyManager()


class Publisher(models.Model):
    company = models.ForeignKey(Company)
    allow_latest_dev = models.BooleanField(default=False)
    domains_blocked = models.BooleanField(default=False)

    objects = models.Manager()
    own = OwnPublisherManager()

I have many more than that. I dislike the copy-paste boilerplate Own(Publisher|Company|Etcetra)Manager). The only change is, as you can see, in the filter.

How can I abstract Own(InsertModelNameHere)Manager and use it from Company, Publisher and other models? I would like to specify the filter kwargs in manager definition.

Upvotes: 1

Views: 2914

Answers (2)

Motiejus Jakštys
Motiejus Jakštys

Reputation: 2979

Inspired by Daniel's reply:

class OwnManager(models.Manager):
    def __init__(self, key):
        super(OwnManager, self).__init__()
        self.k = key

    def get_queryset(self, user):
        if user.is_superuser:
            return super(OwnManager, self).get_queryset()
        return super(OwnManager, self).get_queryset().filter(**{self.k: user})


class Company(models.Model):
    name = models.CharField(max_length=45)

    objects = models.Manager()
    own = OwnManager("companyuser__user")


class Publisher(models.Model):
    company = models.ForeignKey(Company)
    allow_latest_dev = models.BooleanField(default=False)
    domains_blocked = models.BooleanField(default=False)

    objects = models.Manager()
    own = OwnManager("company__companyuser__user")

That way I don't want to create stupid class for every model.

Thanks Daniel.

Upvotes: 1

Daniel Roseman
Daniel Roseman

Reputation: 599630

There's no need for metaprogramming or anything particularly clever here. You just need to extract the bits that change. In this case, it's just the arguments to filter: you can use the dictionary form of argument passing, and store the filter as a string in a class attribute.

class AbstractManager(models.Manager):
    def get_queryset(self, user):
        if user.is_superuser:
            return super(AbstractManager, self).get_queryset()
        return super(AbstractManager, self).get_queryset().filter(**{self.filter: user})

class OwnCompanyManager(AbstractManager):
    filter = "companyuser__user"

class OwnPublisherManager(AbstractManager):
    filter = "company__companyuser__user"

Upvotes: 6

Related Questions