Skip to content

Commit

Permalink
Merge pull request #135 from ryuusenshi/moderatedmodelimproved
Browse files Browse the repository at this point in the history
Enhanced ModeratedModel: embedded Moderator support
  • Loading branch information
dominno committed Jun 4, 2015
2 parents 032a948 + 92b2bef commit 15f5161
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 9 deletions.
82 changes: 74 additions & 8 deletions moderation/db.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,88 @@
import six
"""
This module enables automatic Model registration with custom Moderators
from . import moderation
usage example:
class MyModel(ModeratedModel):
desc = models.TextField()
class Moderator:
notify_user = False
"""
import inspect

from django.utils.six import with_metaclass
from django.db.models import base

from moderation.register import ModerationManager
from moderation.moderator import GenericModerator
from moderation.utils import clear_builtins
from moderation.utils import django_14

moderation = ModerationManager()


class ModeratedModelBase(type):
"""
Metaclass for the ``ModeratedModel`` type
-- automatically registers ``ModeratedModel's``
-- resolves subclass ``Moderator`` into
a instance of ``GenericModerator``
"""
def _resolve_moderator(cls):
"""
``ModeratedModel`` that defines the class Moderator
will have that class resolved into
a class derived from ``GenericModerator``
usage example:
class MyModel(ModeratedModel):
desc = models.TextField()
# ``Moderator`` below will extend ``GenericModerator``
# and will be used when the ``Model`` is registered
class Moderator:
notify_user = False
"""
if hasattr(cls, 'Moderator') and inspect.isclass(cls.Moderator):
Moderator = cls.Moderator
# in python3 __dict__ is dictproxy
attrs = dict(Moderator.__dict__)
attrs = clear_builtins(attrs)

return type(
'%sModerator' % cls.__name__,
(GenericModerator,),
attrs,
)
else:
return None

def __init__(cls, name, bases, clsdict):
"""
Registers ``ModeratedModel``
"""
super(ModeratedModelBase, cls).__init__(name, bases, clsdict)

if (any(x.__name__ == 'ModeratedModel' for x in cls.mro()[1:])):
moderation.register(cls)
moderation.register(cls, cls._resolve_moderator())


class ModelBase(ModeratedModelBase, base.ModelBase):
pass

"""
Common metaclass for ``ModeratedModel`` enabling it to inherit
the behavior of django ``Model`` objects
class ModeratedModel(six.with_metaclass(ModelBase, base.Model)):
"""

class Meta:
abstract = True
if not django_14():
# django.utils.six.with_metaclass is broken in django < 1.5
class ModeratedModel(with_metaclass(ModelBase, base.Model)):
class Meta:
abstract = True
22 changes: 22 additions & 0 deletions moderation/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,29 @@
from distutils.version import StrictVersion


def clear_builtins(attrs):
"""
Clears the builtins from an ``attrs`` dict
Returns a new dict without the builtins
"""
new_attrs = {}

for key in attrs.keys():
if not(key.startswith('__') and key.endswith('__')):
new_attrs[key] = attrs[key]

return new_attrs


def django_17():
if StrictVersion(django.get_version()) >= StrictVersion('1.7.0'):
return True
return False


def django_14():
if StrictVersion(django.get_version()) < StrictVersion('1.5.0'):
return True
return False
8 changes: 8 additions & 0 deletions tests/more_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@

class MyTestModel(ModeratedModel):
name = models.CharField(max_length=20)

class Moderator:
notify_user = False
made_up_value = 'made_up'


class MyTestModelWithoutModerator(ModeratedModel):
name = models.CharField(max_length=20)
32 changes: 31 additions & 1 deletion tests/tests/unit/testmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,14 +455,44 @@ def tearDown(self):
)(ModerateCustomUserTestCase)


@unittest.skipIf(
VERSION[:2] < (1, 5),
"django.utils.six.with_metaclass does not work properly until 1.5"
)
class ModeratedModelTestCase(TestCase):

def tearDown(self):
teardown_moderation()

def test_moderatedmodel_automatic_registration(self):
from tests.more_models import MyTestModel
from tests.more_models import MyTestModelWithoutModerator
from moderation import moderation

registered_models = moderation._registered_models
is_registered = registered_models.get(MyTestModel, None) is not None
# test registration with moderator
moderator = registered_models.get(MyTestModel, None)
is_registered = moderator is not None
self.assertEqual(is_registered, True)
# if Moderator extended then default notify_user should be overwritten
notify_user = moderator.notify_user
self.assertEqual(notify_user, False)
# the value added to the Moderator should also show up
made_up_value = moderator.made_up_value
self.assertEqual(made_up_value, 'made_up')

# test registration without custom moderator
moderator = registered_models.get(MyTestModelWithoutModerator)
self.assertEqual(
moderator.__class__.__name__,
'GenericModerator'
)

def test_django_14(self):
# django_14 test
from mock import patch, Mock
from moderation.utils import django_14
version = Mock()
version.return_value = '1.4.8'
with patch('django.get_version', version):
self.assertTrue(django_14())

0 comments on commit 15f5161

Please sign in to comment.