class MembershipManager(models.Manager): def get_by_type(self, type, user=None): # figure out if we need user or not...
# then call self.get_queryset().get() pass class Membership(models.Model): user = models.ForeignKey('auth.User', related_name='memberships') m_type = models.CharField(max_length=200) objects = MembershipManager()
So... how does get_by_type know if it's being called as "user.memberships.get_by_type(foo)" or "Membership.objects.get_by_type(foo, user=bar)?" Well, I knew that related managers pre-filter on the object they're related to, so I just had to find the code that does that.
It's right here in the ForeignRelatedObjectsDescriptor class. The interesting bit is:
def related_manager_cls(self): # Dynamically create a class that subclasses the related model's default # manager. superclass = self.related.model._default_manager.__class__ rel_field = self.related.field rel_model = self.related.model class RelatedManager(superclass): def __init__(self, instance): super(RelatedManager, self).__init__() self.instance = instance self.core_filters= {'%s__exact' % rel_field.name: instance} self.model = rel_model
This is a class factory that, in this case, subclasses MembershipManager when creating the attribute user.memberships (actually, that's not quite true. user.memberships is actually the ForeignRelatedObjectsDescriptor which hands out this dynamically created, and unnamed, subclass of MembershipManager. The descriptor is used as a proxy so that User.memberships doesn't return anything that actually is a related manager because it would be misleading at best.)
So, long story short, if self.instance exists when `get_by_type` is called, we're in a related object subclass and you can check if self.instance is a User object, in which case you can then allow the caller to skip the user keyword argument.
This is probably documented somewhere on the Django site, but I couldn't find it easily.