AlexPham
AlexPham

Reputation: 321

Django - Simplify Proxy Model to single class

I have User table in my DB, they can be active or inactive. If I only want to query on active user, I define a Proxy Model like following.

class User(models.Model):
    name        = models.CharField(max_length=50)
    location    = models.CharField(max_length=50)
    active      = models.BooleanField()


class UserActive(models.Manager):
    def get_queryset(self):
        return super(UserActive, self).get_queryset().filter(active=True)

class ActiveUser(User):
    objects = UserActive()
    class Meta:
        proxy = True

Then by working with ActiveUser, I can do my calculation/statistic with only active user.

The problem is, I need to define both UserActive and ActiveUser class, it seems awkward to me. Because with each main class (in this case is User), we need to define two other classes. Imaging we have several other model need to implement Proxy, the code would look messy. May I know if we can have more elegant way ?

Thanks Alex

Upvotes: 2

Views: 66

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476659

I would really avoid overwriting the .objects manager, and use this as some sort of implicit filtering. The Zen of Python is explicit is better than implicit, by using ActiveUser, you basically implement a filtering manager, but propose it like the entire set.

Perhaps a more elegant solution is to define multiple managers. So we can construct a filtering manager decorator:

def filter_manager(**kwargs):
    def decorator(klass):
        def get_queryset(self):
            return super(klass, self).get_queryset().filter(**kwargs)
        klass.get_queryset = get_queryset
        return klass
    return decorator

This decorator will however throw away a get_queryset that is defined on the manager itself, so you can not perform an extra patch with this.

Now we can define some managers in a rather elegant way:

@filter_manager(active=True)
class ActiveManager(models.Manager):
    pass

@filter_manager(active=False)
class InactiveManager(models.Manager):
    pass

Finally we can add these managers to the User model, and use explicit names:

class User(models.Model):
    name        = models.CharField(max_length=50)
    location    = models.CharField(max_length=50)
    active      = models.BooleanField()

    objects = models.Manager()
    active_users = ActiveManager()
    inactive_users = InactiveManager()

So now we can use User.active_users to query for the active users. We thus have no proxy models, and can query with User.active_users.count() for example (well we can perform all operations like with .objects but then for .active_users.

Upvotes: 2

AlexPham
AlexPham

Reputation: 321

I created a new Django project, with only User model. My models.py look like this

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

# Create your models here.

def filter_manager(**kwargs):
    def decorator(klass):
        def get_queryset(self):
            return super(klass, self).get_queryset().filter(**kwargs)
        klass.get_queryset = get_queryset
        return klass
    return decorator


@filter_manager(active=True)
class ActiveManager(models.Manager):
    pass

@filter_manager(active=False)
class InactiveManager(models.Manager):
    pass

class User(models.Model):
    name        = models.CharField(max_length=50)
    location    = models.CharField(max_length=50)
    active      = models.BooleanField()
    active_user = ActiveManager()

When I tried User.objects.all().

Error: type object 'User' has no attribute 'objects'

Upvotes: 0

Related Questions