Provide dir() support for PassThroughManagers.

Reported in #55 by erikcw.
This commit is contained in:
Keryn Knight 2013-11-24 14:16:18 +00:00
parent b5b5864d5f
commit 8a1d0662f1
3 changed files with 54 additions and 0 deletions

View file

@ -4,6 +4,12 @@ CHANGES
master (unreleased)
-------------------
* ``PassThroughManager`` now has support for seeing exposed methods via
``dir``, allowing `IPython`_ tab completion to be useful. Merge of GH-104,
fixes GH-55.
.. _IPython: http://ipython.org/
2.0.3 (2014.03.19)
-------------------

View file

@ -244,6 +244,20 @@ class PassThroughManagerMixin(object):
return getattr(self.get_query_set(), name)
return getattr(self.get_queryset(), name)
def __dir__(self):
"""
Allow introspection via dir() and ipythonesque tab-discovery.
We do dir(type(self)) because to do dir(self) would be a recursion
error.
We call dir(self.get_query_set()) because it is possible that the
queryset returned by get_query_set() is interesting, even if
self._queryset_cls is None.
"""
my_values = frozenset(dir(type(self)))
my_values |= frozenset(dir(self.get_query_set()))
return list(my_values)
def get_queryset(self):
try:
qs = super(PassThroughManagerMixin, self).get_queryset()

View file

@ -1235,6 +1235,40 @@ class PassThroughManagerTests(TestCase):
self.assertFalse(hasattr(dude.cars_owned, 'by_name'))
def test_using_dir(self):
# make sure introspecing via dir() doesn't actually cause queries,
# just as a sanity check.
with self.assertNumQueries(0):
querysets_to_dir = (
Dude.objects,
Dude.objects.by_name('Duder'),
Dude.objects.all().by_name('Duder'),
Dude.abiders,
Dude.abiders.rug_positive(),
Dude.abiders.all().rug_positive()
)
for qs in querysets_to_dir:
self.assertTrue('by_name' in dir(qs))
self.assertTrue('abiding' in dir(qs))
self.assertTrue('rug_positive' in dir(qs))
self.assertTrue('rug_negative' in dir(qs))
# some standard qs methods
self.assertTrue('count' in dir(qs))
self.assertTrue('order_by' in dir(qs))
self.assertTrue('select_related' in dir(qs))
# make sure it's been de-duplicated
self.assertEqual(1, dir(qs).count('distinct'))
# manager only method.
self.assertTrue('get_stats' in dir(Dude.abiders))
# manager only method shouldn't appear on the non AbidingManager
self.assertFalse('get_stats' in dir(Dude.objects))
# standard manager methods
self.assertTrue('get_query_set' in dir(Dude.abiders))
self.assertTrue('contribute_to_class' in dir(Dude.abiders))
class CreatePassThroughManagerTests(TestCase):
def setUp(self):
self.dude = Dude.objects.create(name='El Duderino')