mirror of
https://github.com/Hopiu/django-model-utils.git
synced 2026-03-16 20:00:23 +00:00
manager_from bugfix from George Sakkis; fixes #1
This commit is contained in:
parent
afafdd2bf9
commit
5da38ee0c6
4 changed files with 39 additions and 13 deletions
|
|
@ -4,6 +4,7 @@ CHANGES
|
|||
tip (unreleased)
|
||||
----------------
|
||||
|
||||
- incorporated manager_from inherited queryset bugfix (thanks George Sakkis)
|
||||
- added manager_from (thanks George Sakkis)
|
||||
- added StatusField, MonitorField, TimeFramedModel, and StatusModel
|
||||
(thanks Jannis Leidel)
|
||||
|
|
|
|||
|
|
@ -41,26 +41,34 @@ def manager_from(*mixins, **kwds):
|
|||
:keyword manager_cls: The base manager class to extend from
|
||||
(``django.db.models.manager.Manager`` by default).
|
||||
'''
|
||||
# collect separately the mixin classes and methods
|
||||
bases = [kwds.get('queryset_cls', QuerySet)]
|
||||
attrs = {}
|
||||
methods = {}
|
||||
for mixin in mixins:
|
||||
if isinstance(mixin, (ClassType, type)):
|
||||
bases.append(mixin)
|
||||
else:
|
||||
try: attrs[mixin.__name__] = mixin
|
||||
try: methods[mixin.__name__] = mixin
|
||||
except AttributeError:
|
||||
raise TypeError('Mixin must be class or function, not %s' %
|
||||
mixin.__class__)
|
||||
# create the QuerySet subclass
|
||||
id = hash(mixins + tuple(kwds.iteritems()))
|
||||
qset_cls = type('Queryset_%d' % id, tuple(bases), attrs)
|
||||
new_queryset_cls = type('Queryset_%d' % id, tuple(bases), methods)
|
||||
# create the Manager subclass
|
||||
bases[0] = kwds.get('manager_cls', Manager)
|
||||
def _get_query_set(self):
|
||||
if hasattr(self, '_db'):
|
||||
return qset_cls(self.model, using=self._db)
|
||||
else:
|
||||
return qset_cls(self.model)
|
||||
attrs['get_query_set'] = _get_query_set
|
||||
manager_cls = type('Manager_%d' % id, tuple(bases), attrs)
|
||||
return manager_cls()
|
||||
bases[0] = manager_cls = kwds.get('manager_cls', Manager)
|
||||
new_manager_cls = type('Manager_%d' % id, tuple(bases), methods)
|
||||
# and finally override new manager's get_query_set
|
||||
super_get_query_set = manager_cls.get_query_set
|
||||
def get_query_set(self):
|
||||
# first honor the super manager's get_query_set
|
||||
qs = super_get_query_set(self)
|
||||
# and then try to bless the returned queryset by reassigning it to the
|
||||
# newly created Queryset class, though this may not be feasible
|
||||
if not issubclass(new_queryset_cls, qs.__class__):
|
||||
raise TypeError('QuerySet subclass conflict: cannot determine a '
|
||||
'unique class for queryset instance')
|
||||
qs.__class__ = new_queryset_cls
|
||||
return qs
|
||||
new_manager_cls.get_query_set = get_query_set
|
||||
return new_manager_cls()
|
||||
|
|
|
|||
|
|
@ -83,8 +83,20 @@ class PublishedMixin(object):
|
|||
def unpublished(self):
|
||||
return self.filter(published=False)
|
||||
|
||||
class ByAuthorQuerySet(models.query.QuerySet, AuthorMixin):
|
||||
pass
|
||||
|
||||
class FeaturedManager(models.Manager):
|
||||
def get_query_set(self):
|
||||
return ByAuthorQuerySet(self.model, using=self._db).filter(feature=True)
|
||||
|
||||
class Entry(models.Model):
|
||||
author = models.CharField(max_length=20)
|
||||
published = models.BooleanField()
|
||||
feature = models.BooleanField(default=False)
|
||||
|
||||
objects = manager_from(AuthorMixin, PublishedMixin, unpublished)
|
||||
broken = manager_from(PublishedMixin, manager_cls=FeaturedManager)
|
||||
featured = manager_from(PublishedMixin,
|
||||
manager_cls=FeaturedManager,
|
||||
queryset_cls=ByAuthorQuerySet)
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ class ManagerFromTests(TestCase):
|
|||
def setUp(self):
|
||||
Entry.objects.create(author='George', published=True)
|
||||
Entry.objects.create(author='George', published=False)
|
||||
Entry.objects.create(author='Paul', published=True)
|
||||
Entry.objects.create(author='Paul', published=True, feature=True)
|
||||
|
||||
def test_chaining(self):
|
||||
self.assertEqual(Entry.objects.by_author('George').published().count(),
|
||||
|
|
@ -381,3 +381,8 @@ class ManagerFromTests(TestCase):
|
|||
def test_typecheck(self):
|
||||
self.assertRaises(TypeError, manager_from, 'somestring')
|
||||
|
||||
def test_custom_get_query_set(self):
|
||||
self.assertEqual(Entry.featured.published().count(), 1)
|
||||
|
||||
def test_cant_reconcile_qs_class(self):
|
||||
self.assertRaises(TypeError, Entry.broken.all)
|
||||
|
|
|
|||
Loading…
Reference in a new issue