Skylar Saveland
Skylar Saveland

Reputation: 11434

Django custom manager with RelatedManager

There must be a problem with super(InviteManager, self).get_query_set() here but I don't know what to use. When I look through the RelatedManager of a user instance,

len(Invite.objects.by_email()) == len(user.invite_set.by_email())

Even if the user does not have any invites. However, user.invite_set.all() correctly returns all of the Invite objects that are keyed to the User object.

class InviteManager(models.Manager):
    """with this we can get the honed querysets like user.invite_set.rejected"""

    use_for_related_fields = True

    def by_email(self):
        return super(InviteManager, self).get_query_set().exclude(email='')

class Invite(models.Model):
    """an invitation from a user to an email address"""

    user = models.ForeignKey('auth.User', related_name='invite_set')
    email = models.TextField(blank=True)
    objects = InviteManager()

'''
u.invite_set.by_email() returns everything that Invite.objects.by_email() does
u.invite_set.all() properly filters Invites and returns only those where user=u
'''

Upvotes: 2

Views: 4493

Answers (4)

the_drow
the_drow

Reputation: 19171

The documentation specifies that you should not filter the queryset using get_query_set() when you replace the default manager for related sets.

Do not filter away any results in this type of manager subclass

One reason an automatic manager is used is to access objects that are related to from some other model. In those situations, Django has to be able to see all the objects for the model it is fetching, so that anything which is referred to can be retrieved.

If you override the get_query_set() method and filter out any rows, Django will return incorrect results. Don’t do that. A manager that filters results in get_query_set() is not appropriate for use as an automatic manager.

Upvotes: 1

George Griffin
George Griffin

Reputation: 744

Try using .all() in place of .get_query_set(). That seemed to do the trick for a similar problem I was having.

def by_email(self):
    return super(InviteManager, self).all().exclude(email='')

Upvotes: 0

tcarobruce
tcarobruce

Reputation: 3838

You may want a custom QuerySet that implements a by_email filter. See examples on Subclassing Django QuerySets.

class InviteQuerySet(models.query.QuerySet):
    def by_email(self):
       return self.exclude(email='')

class InviteManager(models.Manager):
    def get_query_set(self):
        model = models.get_model('invite', 'Invite')
        return InviteQuerySet(model)

Upvotes: 3

SmileyChris
SmileyChris

Reputation: 10794

Try:

def by_email(self):
    return super(InviteManager, self).exclude(email='')

If nothing else, the .get_query_set() is redundant. In this case, it may be returning a whole new queryset rather than refining the current one.

Upvotes: 2

Related Questions