Coder Social home page Coder Social logo

dominno / django-moderation Goto Github PK

View Code? Open in Web Editor NEW
268.0 9.0 91.0 628 KB

django-moderation is reusable application for Django framework, that allows to moderate any model objects.

License: BSD 3-Clause "New" or "Revised" License

Python 98.51% Makefile 0.09% HTML 1.40%

django-moderation's Introduction

Introduction

https://travis-ci.org/dominno/django-moderation.png https://coveralls.io/repos/dominno/django-moderation/badge.png?branch=master

django-moderation is reusable application for Django framework, that allows to moderate any model objects.

Possible use cases:

  • User creates his profile, profile is not visible on site. It will be visible on site when moderator approves it.
  • User change his profile, old profile data is visible on site. New data will be visible on site when moderator approves it.

Features:

  • configurable admin integration(data changed in admin can be visible on site when moderator approves it)
  • moderation queue in admin
  • html differences of changes between versions of objects
  • configurable email notifications
  • custom model form that allows to edit changed data of object
  • auto approve/reject for selected user groups or user types
  • support for ImageField model fields on moderate object page
  • 100% PEP8 correct code
  • test coverage > 80%

Requirements

Python 3.6, 3.7, 3.8, 3.9

Django 2.2, 3.1, 3.2

Known issues

  • m2m relations in models are not currently supported

Documentation

Full documentation is hosted at ReadTheDocs django-moderation

Contributors

Special thanks to all persons that contributed to this project.

Thank you for all ideas, bug fixes, patches, maintaining.

django-moderation's People

Contributors

aaw64 avatar adityar7 avatar aramgutang avatar blag avatar brunosmartin avatar cornelius-keller avatar danielbraun avatar dmytrolitvinov avatar dominno avatar dulacp avatar ebrelsford avatar martibosch avatar maybethisisru avatar movermeyer avatar phillipberndt avatar qris avatar ryuusenshi avatar sahilgupta avatar samjacobclift avatar samtshaw avatar sebastianclarke avatar sonthonaxrk avatar theinkvi avatar tomasd avatar treyhunner avatar zbyte64 avatar zypro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-moderation's Issues

ModerationAdmin and pre-existing objects

If you actually utilize ModerationAdmin (I cannot because of multiple inheritance with GrappelliModelAdmin breaking Grappelli)... You cannot access objects that existed before you installed django-moderation.

Traceback:

File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  100.                     response = callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
  240.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  74.                     response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  69.         response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/sites.py" in inner
  194.             return view(request, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/moderation/admin.py" in change_view
  40.             self.send_message(request, object_id)
File "/usr/local/lib/python2.7/site-packages/moderation/admin.py" in send_message
  45.         moderated_object = ModeratedObject.objects.get(object_pk=object_id)
File "/usr/local/lib/python2.7/site-packages/django/db/models/manager.py" in get
  132.         return self.get_query_set().get(*args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/db/models/query.py" in get
  339.                     % self.model._meta.object_name)

Exception Type: DoesNotExist at /admin/content/content/12/
Exception Value: ModeratedObject matching query does not exist.

Solution: Try/Except inside send_message() method in ModerationAdmin. Maybe an additional message could be added, like "This object is not registered with the moderation system."

Edit moderate_object.html admin template in external app

Hello everyone!
I'm using django-moderation and geodjango together in one personal project.
I would need to edit the moderate_object.html admin template in order to render a map in all GEO fields (Points, PolyLines, etc). Actually the are rendered like plain text. I think that its because the serialization inside ModeratedObject class.
Can you explain me where should I put the new .html file in my project's directory tree?
Thanks!

maximum recursion depth exceeded while calling a Python object

While trying to save an object, I get:

RuntimeError at /adm/mg_productos/producto/360/
maximum recursion depth exceeded while calling a Python object

The looping part is in moderation._init line 316:

File "C:\areaweb\microgeo\microgeo\moderation\__init__.py" in post_save_handler
  316.                 moderated_object.changed_object.save(
File "C:\Python26\lib\site-packages\django\db\models\base.py" in save
  410.         self.save_base(force_insert=force_insert, force_update=force_update)
File "C:\Python26\lib\site-packages\django\db\models\base.py" in save_base
  506.                 created=(not record_exists), raw=raw)
File "C:\Python26\lib\site-packages\django\dispatch\dispatcher.py" in send
  166.             response = receiver(signal=self, sender=sender, **named)
File "C:\areaweb\microgeo\microgeo\moderation\__init__.py" in post_save_handler
  316.                 moderated_object.changed_object.save()

Here's the model I am trying to save:

class Producto(BaseModel):
    """(Producto description)"""
    categoria = models.ForeignKey(Categoria,
                                  blank=True, null=True,
                                  related_name="productos",
                                  help_text=u"Categoría bajo la que está el\
                                              producto")

    grupo = models.ForeignKey(Grupo, related_name='productos', blank=True, null=True)

    nombre = models.CharField(max_length=200)
    tipo = models.CharField(max_length=2,
                            choices=(('PR', 'Producto',),
                                     ('CP', 'Curso')),
                            default='PR')
    slug = models.SlugField("Nombre para URL", unique=True)
    marca = models.ForeignKey("Marca", blank=True, null=True)
    descripcion = HTMLField()

    imagen_principal = fields.ImageWithThumbnailsField(upload_to="productos/imagenes",
                                                       blank = True, null = True,
                                                       thumbnail = dict(
                                                           size=(270, 231)
                                                       ),
                                                       extra_thumbnails = extra_thumbs,
                                                       help_text=\
                                                       "Esta será la imagen aparecerá a un\
                                                       costado de la descripción",
                                                      )

    imagen = fields.ImageWithThumbnailsField(upload_to="productos/imagenes",
                                             blank = True, null = True,
                                             thumbnail = dict(
                                                 size=(270, 231)
                                             ),
                                             extra_thumbnails = extra_thumbs,
                                             help_text=\
                                             "Esta será la imagen aparecerá a un\
                                             costado de la descripción",
                                            )

    ficha_tecnica = FilteredFileField(upload_to="productos/fichas",
                                      allowed={'pdf': 'application/pdf'},
                                      max_length=100, blank=True, null=True,
                                      help_text=u"Se permiten sólo archivos PDF")

    #archivos = generic.GenericRelation('ArchivoConTipo')

    #imagenes = generic.GenericRelation(Imagen)
    videos = generic.GenericRelation(Video)

    relacionados = models.ManyToManyField('self',
                                          related_name='relacionado_con',
                                          blank=True, null=True)

    complementos = models.ManyToManyField(Categoria,
                                          related_name='complemento_de',
                                          null=True, blank=True,
                                          limit_choices_to={
                                              'padre__isnull': False,
                                              'padre__padre__isnull': True,
                                          })

    orden = models.PositiveIntegerField(default=1)

    #galeria = models.OneToOneField('photologue.Gallery', related_name='producto')

    def rowlevelpermission(self, user):
        return self.categoria.rowlevelpermission(user)


    class Meta:
        ordering = ('categoria', 'grupo', 'orden', 'nombre')
        verbose_name = "Producto o Servicio"
        verbose_name_plural = "Productos y Servicios"

    def __unicode__(self):
        return u"%s" % (self.nombre)

    @models.permalink
    def get_absolute_url(self):
        return ('productos.producto', (self.slug,))

    def get_grupo_name(self):
        """Obtiene el nombre del grupo y nada si es None"""
        if self.grupo is None:
            return u''
        return u'%s' % (self.grupo)

    def get_categoria_name(self):
        """Obtiene el nombre de la categoria, o none"""
        if self.categoria:
            return u"%s" % self.categoria.nombre
        return u""

    def area(self):
        if self.categoria:
            return u"%s" % self.categoria.get_root()

Approve/Reject not working

When I first create an object in the admin, it works just fine. It automoderates just fine, using the save() method of ModeratedObject. However, Django/Python stall when I click "Approve" or "Reject" from the ModeratedObject admin, and I eventually have to shut down my dev server (I'm using the one built into Django). No errors are ever outputted and it eventually crashes the server by itself.

Something must either be wrong with the _moderate() method on the model, or with one of the pre/post moderation signals.

Implement simple moderation history for given object.

Moderator user can view simple moderation history for given object. On moderation history page user can see:

  • date of moderation action
  • user name of user that has moderated this object
  • moderation reason
  • type of moderation action - approved / rejected

This could be implemented using LogEntry model:

http://stackoverflow.com/questions/987669/tying-in-to-django-admins-model-history

Moderation history would be available after clicking History button on moderate page of given object.

Pass object to `is_auto_*` for more useful custom auto-moderation

This will allow for custom auto-moderation methods that would allow for per-object and per-model moderation checks. The methods already exist, they just need the extra obj parameter, and to have this custom auto-approve functionality documented.

Example:

# Inside MyModelModerator, which is registered with MyModel
def is_auto_reject(self, obj, user):
    # Auto reject spam
    if akismet_spam_check(obj.body):  # Check body of object for spam
        # Body of object is spam, moderate
        return True
    super(MyModelModerator, self).is_auto_reject(obj, user)

For the exact object to pass in, I'm not sure off hand which is smarter to pass... The SerializedObject in changed_object or the original object in content_object? I guess it depends on the other logic, if it's new or changed at that moment.

Implement Flagging

Implement Flagging and Moderation on Flagging options

Flagging would utilize another moderation status (MODERATION_STATUS_FLAGGED), and would be useful for allowing registered users to warn the staff of inappropriate content. A notification could be sent to moderators warning that "_______ Object has been Flagged for review". Then also, "Moderation on Flagged" if set in the model's Moderator could allow the moderation manager to hide objects if they have been Flagged.

Either boolean is_flagged field, or flag_count integer field could work for this idea... flag_count would give the greater flexibility of allowing an object to be flagged X times before hiding it from public view. This could also be denormalized, like the optional visible column on the Model, for performance reasons.

moderation crashes on models with DecimalField

I'm trying to moderate the creation of a django-shop Product model. This model has a field 'unit_price', which is a CurrencyField, which is just a fancy DecimalField (see https://github.com/divio/django-shop/blob/master/shop/util/fields.py#L6)

When I create a new product, I get the following error:

Traceback:
File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/core/handlers/base.py" in get_response

  1.                     response = middleware_method(request, callback, callback_args, callback_kwargs)
    
    File "/home/martin/Envs/marketto/src/ajaxmiddleware/ajaxmiddleware/middleware.py" in process_view
  2.         return new_callback(request, _callback_args, *_callback_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/views/generic/base.py" in view
  3.         return self.dispatch(request, _args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/utils/decorators.py" in _wrapper
  4.         return bound_func(_args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  5.             return view_func(request, _args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/utils/decorators.py" in bound_func
  6.             return func(self, _args2, *_kwargs2)
    
    File "/home/martin/Projects/aquasys/marketto/vivitz/vehicle_shop/views/crud.py" in dispatch
  7.     return super(VehicleCreateView, self).dispatch(_args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/views/generic/base.py" in dispatch
  8.     return handler(request, _args, *_kwargs)
    
    File "/home/martin/Envs/marketto/src/ajaxmiddleware/ajaxmiddleware/views.py" in post
  9.             return super(HybridView, self).post(self, **kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/views/generic/edit.py" in post
  10.     return super(BaseCreateView, self).post(request, _args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/views/generic/edit.py" in post
  11.         return self.form_valid(form)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/views/generic/edit.py" in form_valid
  12.     self.object = form.save()
    
    File "/home/martin/Projects/aquasys/marketto/vivitz/vehicle_shop/forms.py" in save
  13.     return super(VehicleForm, self).save()
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/shop_simplecategories/admin.py" in save
  14.   product.save()
    
    File "/home/martin/Projects/aquasys/marketto/vivitz/vehicle_shop/models.py" in save
  15.     super(Vehicle, self).save(_args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/polymorphic/polymorphic_model.py" in save
  16.     return super(PolymorphicModel, self).save(_args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/db/models/base.py" in save
  17.     self.save_base(using=using, force_insert=force_insert, force_update=force_update)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/db/models/base.py" in save_base
  18.             created=(not record_exists), raw=raw, using=using)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/dispatch/dispatcher.py" in send
  19.         response = receiver(signal=self, sender=sender, **named)
    
    File "/home/martin/Envs/marketto/src/moderation/src/moderation/register.py" in post_save_handler
  20.         moderator.inform_moderator(instance)
    
    File "/home/martin/Envs/marketto/src/moderation/src/moderation/moderator.py" in inform_moderator
  21.               recipient_list=MODERATORS)
    
    File "/home/martin/Envs/marketto/src/moderation/src/moderation/moderator.py" in send
  22.             'moderated_object': content_object.moderated_object,
    
    File "/home/martin/Envs/marketto/src/moderation/src/moderation/register.py" in get_moderated_object
  23.                                              '_relation_object').get()
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/db/models/manager.py" in get
  24.     return self.get_query_set().get(_args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/db/models/query.py" in get
  25.     num = len(clone)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/db/models/query.py" in len
  26.             self._result_cache = list(self.iterator())
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/db/models/query.py" in iterator
  27.                 obj = model(*row[index_start:aggregate_start])
    
    File "/home/martin/Envs/marketto/src/moderation/src/moderation/models.py" in init
  28.     super(ModeratedObject, self).**init**(_args, *_kwargs)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/db/models/base.py" in init
  29.     signals.post_init.send(sender=self.**class**, instance=self)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/dispatch/dispatcher.py" in send
  30.         response = receiver(signal=self, sender=sender, **named)
    
    File "/home/martin/Envs/marketto/src/moderation/src/moderation/fields.py" in post_init
  31.                         self._deserialize(value))
    
    File "/home/martin/Envs/marketto/src/moderation/src/moderation/fields.py" in _deserialize
  32.     for parent in obj_generator:
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/core/serializers/json.py" in Deserializer
  33. for obj in PythonDeserializer(simplejson.load(stream), **options):
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/core/serializers/python.py" in Deserializer
  34.             data[field.name] = field.to_python(field_value)
    
    File "/home/martin/Envs/marketto/lib/python2.6/site-packages/django/db/models/fields/init.py" in to_python
  35.         return decimal.Decimal(value)
    
    File "/usr/lib/python2.6/decimal.py" in new
  36.                         "First convert the float to a string")
    

Exception Type: TypeError at /shop/products/create/
Exception Value: Cannot convert float to Decimal. First convert the float to a string

I really have no clue why there is a Float involved at all... Are there known issues with DecimalFields?

`admin_integration_enabled` Typo

Should be admin_integration_enabled instead of admin_intergration_enabled in all instances.

Also, moderatated_by typo still occurs in documentation (README.rst), leftover from bd14749

RegistrationError at /admin/ <class 'kurse.models.kurs'> has been registered with Moderation.

Hi,

I installed and configured moderation according to the readme and get this Error:

RegistrationError at /admin/kurse/kurs/add/

<class 'kurse.models.kurs'> has been registered with Moderation.

I found the same error in Issues here, but could not solve it. I even complety fail to understand why this is an problem, since I am indeed registering this class with Moderation. Maybe you have an idea. Thanks for your Time.

Traceback:
----------------------------------------------------------------------
Environment:

Request Method: GET
Request URL: http://127.0.0.1:8000/admin/
Django Version: 1.2.5
Python Version: 2.6.1
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.admin',
 'django.contrib.admindocs',
 'moderation',
 'kurse',
 'south',
 'photologue',
 'django_filters']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.middleware.csrf.CsrfResponseMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware')


Traceback:
File "C:\Python26\lib\site-packages\django\core\handlers\base.py" in get_response
  91.                         request.path_info)
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py" in resolve
  215.             for pattern in self.url_patterns:
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py" in _get_url_patterns
  244.         patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py" in _get_urlconf_module
  239.             self._urlconf_module = import_module(self.urlconf_name)
File "C:\Python26\lib\site-packages\django\utils\importlib.py" in import_module
  35.     __import__(name)
File "D:\django\bildungswerk\..\bildungswerk\urls.py" in <module>
  3. from kurse.models import kurs
File "D:\django\bildungswerk\kurse\models.py" in <module>
  224. moderation.register(kurs)
File "c:\python26\lib\site-packages\django_moderation-0.2-py2.6.egg\moderation\__init__.py" in register
  175.             raise RegistrationError(msg)

Exception Type: RegistrationError at /admin/
Exception Value: <class 'kurse.models.kurs'> has been registered with Moderation.

DoesNotExist error in Admin for newly created objects

When a new object is created for a model that is under moderation, the new object appears in the admin list view for that model, but clicking through to the admin edit view throws a DoesNotExist error.

This is because moderation/admin.py is trying to calculate an admin message by getting an instance of the object and using the instance to get the ModeratedObject (L55-57 in moderation/admin.py). But it fails to get an object instance because moderation is preventing a successful get() (no previous version exists so get(pk=object_id) fails). This throws the DoesNotExist error.

I made the following change: instead of obtaining an instance of the object and using that to get the ModeratedObject, I'm using ContentType.objects.get_for_model() to get the actual content type for the model and then calling get.

diff --git src/moderation/admin.py src/moderation/admin.py
index c60558a..fe625e1 100644
--- src/moderation/admin.py
+++ src/moderation/admin.py
@@ -52,8 +52,9 @@ class ModerationAdmin(admin.ModelAdmin):

     def send_message(self, request, object_id):
         try:
-            obj = self.model.objects.get(pk=object_id)
-            moderated_obj = ModeratedObject.objects.get_for_instance(obj)
+            content_type = ContentType.objects.get_for_model(self.model)
+            moderated_obj = ModeratedObject.objects.get(object_pk=object_id,
+                                                        content_type=content_type)
             msg = self.get_moderation_message(moderated_obj.moderation_status,
                                               moderated_obj.moderation_reason)
         except ModeratedObject.DoesNotExist:

Hope this is helpful. Great app!

ImageFields in moderated objects acts as empty values when objects are waiting for approval

Steps to reproduce:

  1. create simple model with ImageField
  2. create new item, then save
    Expected: When image is uploaded i can see url in widget

Currently, all ImageFields widgets in objects which are in pending state looks like None value.
we spent few hours on debugging, and our conclusion is, that there is a problem in deserialization: objects returned from deserialization method aren't fully expanded accordingly to model definition.

Need option to disable moderation on object change

Usually once a user has been approved for an object once, they needn't have every future change to that object moderated. Most often the user can be trusted to make changes that do not need constant tracking and approval. Adding a moderate_changes option to GenericModerator, when set to False would reflect this behavior the Model that the Moderator is registered with.

So, an example:

ExampleUser comments on your page. Their comment was not auto-approved (and thus their changes will never be auto-approved given the same auto-approval rules), but upon manually reviewing the comment and approving it, you would like to allow ExampleUser to edit their comment without needing approval for said edit every time. moderate_changes = False would allow this.

Optional denormalization for columns which determine visibility

Currently one query is executed for every object to determine its visibility. Denormalizing this data so that ModeratedObject does not have to be accessed directly for every object would take the N + 1 query issue back down to just one query.

If visibility_column is defined on the Model's moderator, then the Model must have this field already specified and in a compatible field type (any integer). The manager will then first use this option to properly display (or hide) objects which are registered with moderation. Otherwise, the current behavior will act as a fallback (but documentation should urge users to denormalize this data for performance reasons).

(Technically this isn't really denormalization since the visible field should be attached to the Model, but the current method does benefit those who do not want to add any fields to their Models)

Also, for Issue #18, a flagged_column or otherwise-named option should possibly be used to denormalize this data as well, because it will introduce the same performance issues. This depends on the implementation, however. If it were to be implemented where being Moderated on Flagged simple toggles the existing visibility_column to cause the object to be hidden, then this denormalization for Flagging may not be necessary.

Cannot use egg package

I installed version 0.3.2 and it's installed as an egg (zip) file, not a directory. And I think Django is unable access the package fully.

Errors:

On syncdb:

OSError: [Errno 20] Not a directory: '[...]lib/python2.6/site-packages/django_moderation-0.3.2-py2.6.egg/moderation/migrations'

After adding an object:

TemplateDoesNotExist at [...]
moderation/notification_message_moderator.txt

moderation_status value in template

Hello there,

I would like to access moderation_status in template directly from the instance object moderated.
I would have to use something like

  • {{ my_obj.moderation_status }},
  • {{ my_obj.get_moderation_status_display }}
  • or {% if my_obj.moderation_status == 1 %}

How can I achieve this, without having to add boolean field for a visibility_column
(moreover,

  • I need more than a Boolean value, because I have reject, approve and pending possibilities
  • template tag is not the right solution, I also wan't to process the value inside a if tag

)

Thanks,

(Auto-)Approve doesn't work

I've just made the basic installation including moderators.py:

from moderation import moderation
from database.models import ContactPerson

moderation.register(ContactPerson)

also urls.py improvement:

from django.contrib import admin
from moderation.helpers import auto_discover

auto_discover()
admin.autodiscover()

That's it. If I change an object, a moderated object is created. I can also reject it. But if I try to approve it, the status is still "Pending". It also has the mark "Auto-approved: Superuser" and the changes are auto-approved actually, but the status doesn't change. If I try to debug I can see that here:

https://github.com/dominno/django-moderation/blob/master/src/moderation/models.py#L162

self.moderation_status is 1
self.changed_object.moderated_object.moderation_status is 2

Can you help me?
Thanks in advance.
p.s.: Awesome tool!

No module named pep8

I started running the tests to see if everything passed with my configuration, but this seems to be something specific to your config.

Traceback (most recent call last):
  File "manage.py", line 11, in <module>
    execute_manager(settings)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 438, in execute_manager
    utility.execute()
  File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 379, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 196, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 223, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/commands/test.py", line 37, in handle
    failures = test_runner.run_tests(test_labels)
  File "/usr/local/lib/python2.7/site-packages/django/test/simple.py", line 320, in run_tests
    suite = self.build_suite(test_labels, extra_tests)
  File "/usr/local/lib/python2.7/site-packages/django/test/simple.py", line 256, in build_suite
    suite.addTest(build_suite(app))
  File "/usr/local/lib/python2.7/site-packages/django/test/simple.py", line 109, in build_suite
    test_module = get_tests(app_module)
  File "/usr/local/lib/python2.7/site-packages/django/test/simple.py", line 67, in get_tests
    test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
  File "/usr/local/lib/python2.7/site-packages/moderation/tests/__init__.py", line 4, in <module>
    from moderation.tests.test_utils_functions import *
  File "/usr/local/lib/python2.7/site-packages/moderation/tests/test_utils_functions.py", line 8, in <module>
    import pep8
ImportError: No module named pep8

Problem with fields_exclude and auto_approve

Hi,

There is a bug with fields_exclude (or this function is not clear).
I will explain with an example:

I want to avoid moderation of certain fields of a model. For example...

models.py

class Foo(models.Model):
    foo_a = models.CharField(max_length=80)
    foo_b = models.IntegerField

moderator.py

class FooModerator(GenericModerator):
    auto_approve_for_superusers = True
    auto_approve_for_staff = False
    #Fields to exclude from object change list. Default: []
    fields_exclude = ['foo_b']


moderation.register(Foo, FooModerator)

Then.. i expect that when i change field foo_a the moderation will trigger for that field, not for foo_b.

Tests Results:

Case 1

As a superuser, or a user with auto_approve settings enabled, when i change only a field inside fields_exclude list, i never see the change.
That is if i edit the excluded field (foo_b field), then that field is not changed (it still have the old value). Moderation status = Approved.

Case 2 (this works ok):

As a superuser, when i change other fields and a excluded field, it works fine. That is if i edit both, the excluded field and a regular field (foo_a and foo_b), then all the fields have the expected result.
Moderation status = Approved.

Case 3:

Same as case 1 but as a staff user and the same object instance as in case 1 (try it after case 1): The moderation works as expected, that is if i edit only the excluded field, the change can be seeing. Moderation status = Approved.

Case 4:

Same as case 1 but as a staff user and other object instance: The moderation fails with same results as in case 1. Moderation status = Pending.

Case 5:

Continue case 4. When approved, the excluded field (foo_b) is not changed causing loose of data. But.. the next time that object is edited moderation and excluded fields works again!!

I hope I have explained...
Regards,

Mario.

bypass for view and form for special user

Maybe there is a way to achieve this, but I didn't find a solution:
User via UserProfile create instance of a Model A, when they save, I'm not able to show to them (and only them) the instance they have just created (because it's not moderated yet), or even, allow them to modify their submission.

For instance : one user create model A instance, I would like him to be able to re-edit is datas, and to have a sort of preview of what he has just done, not getting a 'instance does'nt exist' for my user only.
(is that clear ?, may be a decorator could do this for the 'owner' of the instance created ?)

thx

"Filter by Content Type" needs only show those types which have been registered.

In ModeratedObjects admin, "Filter by Content Type" should really only allow you to choose those models which have been registered with moderation. Currently this is messy and unusable.

It's my hope that moderation.register() occurs before ModeratedObject initializes, so we can simply use limit_choices_to on the ContentType field.

content_type_limits = {
    'model__in': _get_registered_models()
}

class ModeratedObject(models.Model):
    content_type = models.ForeignKey(ContentType, null=True, blank=True, 
                                     editable=False,
                                     limit_choices_to=content_type_limits)
    ...


def _get_registered_models():
    """
    Generates tuple of models' content types which
    have been registered with moderation
    """
    ???

I know that ModerationManager has a _registered_models dict, but I can't say at the moment if it's the same instance over all places it occurs. I assume so because you instantiate it at the bottom of init.py in the moderation folder, so you should be able to import it from that module in manager/models.py to grab the _registered_models.

I'll test this out myself, it will be a good exercise in my growing Python skills. :)

Documentation: `save_model` override

Need to make explicit that if you have defined your own save_model in your ModelAdmin that you must:

# Custom save_model in MyModelAdmin
def save_model(self, request, obj, form, change):
    # Your custom stuff
    from moderation.helpers import automoderate
    automoderate(obj, request.user)

Otherwise what you save in the admin will get moderated.

Modified content of object erased/reverted after calling my_object.save()

Hi,

I am using te following code to moderate my object:

class NodeModerator(GenericModerator):
    auto_approve_for_superusers = True
    auto_approve_for_staff = True

    visible_until_rejected = False

    visibility_column = 'visible_moderator'

    notify_user = False

    fields_exclude = ['hits', 'author', (and some more fields)] 
moderation.register(Node, NodeModerator)

Then I modify my object and the changes appear in the moderated object admin page.

But when I do:

my_object.author.hits += 1
my_object.author.save()

my_object.hits += 1
my_object.save()

All changes in the moderated object admin disappear/revert back.

Am I doing something wrong or this a bug? And how can I fix it?

Thanks.

with GeoManager

Hello,

I have a problem using django-moderation with a model using models.GeoManager() as manager:
even if objects are flagged as pending or rejected in admin, they are not filtered in queryset, it seems like moderation is bypassed.

also, it could be a save_base(raw=True) pb using geomanager ?

Has someone experienced same problem ?

Assumes "example_project" is installed to PYTHONPATH

example_project exists outside the "moderation" folder. I've symlinked the moderation folder from my repository path to my site-packages directory, so it doesn't exist on the PYTHONPATH.

You should include the "example_app" inside of the tests directory, then, and use that for all the models and fixtures you need to carry out tests.

Traceback (most recent call last):
  File "manage.py", line 11, in <module>
    execute_manager(settings)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 438, in execute_manager
    utility.execute()
  File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 379, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 196, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 223, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/commands/test.py", line 37, in handle
    failures = test_runner.run_tests(test_labels)
  File "/usr/local/lib/python2.7/site-packages/django/test/simple.py", line 320, in run_tests
    suite = self.build_suite(test_labels, extra_tests)
  File "/usr/local/lib/python2.7/site-packages/django/test/simple.py", line 256, in build_suite
    suite.addTest(build_suite(app))
  File "/usr/local/lib/python2.7/site-packages/django/test/simple.py", line 109, in build_suite
    test_module = get_tests(app_module)
  File "/usr/local/lib/python2.7/site-packages/django/test/simple.py", line 67, in get_tests
    test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
  File "/usr/local/lib/python2.7/site-packages/moderation/tests/__init__.py", line 5, in <module>
    from moderation.tests.test_admin import *
  File "/usr/local/lib/python2.7/site-packages/moderation/tests/test_admin.py", line 11, in <module>
    from example_project.example_app.models import ExampleUserProfile
ImportError: No module named example_project.example_app.models

PyPi package outdated?

Just did pip install django-moderation.

The helerps.py only has "automoderate" method. Must be an old version?

Unapproved objects made visible when changed multiple times

Scenario:
A is approved
A is changed to A'.
A' is now pending approval.
A is made visible at this point.
A' is changed to A''.
A'' is now pending approval.
A' is made visible at this point.

The problem in this scenario is where A' is made visible without approval. The expected behavior here would be A is made visible. With a put-up policy (the current policy supported), objects pending approval should never be publicized before approval.

Add "pending_column" for performance when checking if pending moderation

I'm working out how to add a "pending_column" to improve performance when you want to check which of a large number of moderated model instances are pending moderation. I added a 'pending' Boolean field to my model and referenced it as my 'pending_column' when I subclassed moderator.GenericModerator.

As a first step, in register.py pre_save_handler I added:

if moderator.pending_column:
    setattr(instance, moderator.pending_column, True)

After saving a change to a model instance, on viewing this instance in the admin, I can see the 'pending' attribute appropriately set as True, but when I do mymodel_instance.pending, it strangely returns False. I'm not sure what's going on here, likely something to do with the manager, any idea?

DeprecationWarning in Django 1.3

~/dev/venvs/test/moderation/fields.py:7: DeprecationWarning: A Field class whose db_type method hasn't been updated to take a connection argument.

django moderation email notify not working

Added Django-moderation app to my application and it works. But mail notification for moderator and user not working.

Added settings like below:-
MODERATORS = [ 'email']

notify_moderator and notify_email both default is True.

Error in admin view when there is no associated file for ImageField

I encountered the error below in the admin page for moderated object. This happens when moderated object has an image field but associated file is not uploaded.

TemplateSyntaxError at /admin/moderation/moderatedobject/3/
Caught ValueError while rendering: The 'foo' attribute has no file associated with it.

Unable to import fixtures on model registered with moderation.

I tried running manage syncdb (UPDATE: It was actually manage test) and received a ~3400 line traceback...

Most of it is a repeating pattern relating to moderation, I've cut out the pattern and just left one, but the pattern repeats for over 3000 lines...

Problem installing fixture:

  File "/usr/local/lib/python2.7/site-packages/django/core/management/commands/loaddata.py", line 169, in handle
    obj.save(using=using)
  File "/usr/local/lib/python2.7/site-packages/django/core/serializers/base.py", line 165, in save
    models.Model.save_base(self.object, using=using, raw=True)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/base.py", line 459, in save_base
    signals.pre_save.send(sender=origin, instance=self, raw=raw)
  File "/usr/local/lib/python2.7/site-packages/django/dispatch/dispatcher.py", line 166, in send
    response = receiver(signal=self, sender=sender, **named)

  ## Start pattern

  File "/usr/local/lib/python2.7/site-packages/moderation/__init__.py", line 245, in pre_save_handler
    moderated_object.save()
  File "/usr/local/lib/python2.7/site-packages/moderation/models.py", line 77, in save
    auto_save=False)
  File "/usr/local/lib/python2.7/site-packages/moderation/models.py", line 130, in approve
    auto_save)
  File "/usr/local/lib/python2.7/site-packages/moderation/models.py", line 113, in _moderate
    self.changed_object.save()
  File "/usr/local/lib/python2.7/site-packages/django/db/models/base.py", line 435, in save
    self.save_base(using=using, force_insert=force_insert, force_update=force_update)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/base.py", line 459, in save_base
    signals.pre_save.send(sender=origin, instance=self, raw=raw)
  File "/usr/local/lib/python2.7/site-packages/django/dispatch/dispatcher.py", line 166, in send
    response = receiver(signal=self, sender=sender, **named)

  ## End pattern

  File "/usr/local/lib/python2.7/site-packages/moderation/__init__.py", line 257, in _get_or_create_moderated_object
    moderated_object = ModeratedObject.objects.get(object_pk=pk)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/manager.py", line 132, in get
    return self.get_query_set().get(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/query.py", line 334, in get
    num = len(clone)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/query.py", line 79, in __len__
    self._result_cache = list(self.iterator())
  File "/usr/local/lib/python2.7/site-packages/django/db/models/query.py", line 267, in iterator
    for row in compiler.results_iter():
  File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 674, in results_iter
    for rows in self.execute_sql(MULTI):
  File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 719, in execute_sql
    sql, params = self.as_sql()
  File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 69, in as_sql
    where, w_params = self.query.where.as_sql(qn=qn, connection=self.connection)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/where.py", line 91, in as_sql
    sql, params = child.as_sql(qn=qn, connection=connection)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/where.py", line 94, in as_sql
    sql, params = self.make_atom(child, qn, connection)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/where.py", line 141, in make_atom
    lvalue, params = lvalue.process(lookup_type, params_or_value, connection)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/where.py", line 288, in process
    connection=connection, prepared=True)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 350, in get_db_prep_lookup
    return [self.get_db_prep_value(value, connection=connection, prepared=prepared)]
  File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
RuntimeError: maximum recursion depth exceeded in __instancecheck__

Then after it repeats hundreds of times it continues on over several files to the end of the traceback.

{% csrf_token %} needed in form templates

Submitting forms fails if CSRF Middleware is installed.

moderate_object.html, Line 37 should be:

<form enctype="multipart/form-data" action="" method="post" id="moderatedobject_form">{% csrf_token %}

TypeError ... Expected string or buffer

Traceback:
File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
100. response = callback(request, _callback_args, *_callback_kwargs)File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
240. return self.admin_site.admin_view(view)(_args, *_kwargs)File "/usr/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
74. response = view_func(request, _args, *_kwargs)File "/usr/local/lib/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
69. response = view_func(request, _args, *_kwargs)File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/sites.py" in inner
194. return view(request, _args, *_kwargs)File "/usr/local/lib/python2.7/site-packages/moderation/admin.py" in change_view
115. changed_object)File "/usr/local/lib/python2.7/site-packages/moderation/diff.py" in generate_diff
80. 'diff': html_diff(change1, change2)}File "/usr/local/lib/python2.7/site-packages/moderation/diff.py" in html_diff
49. a, b = html_ta_list(a), html_ta_list(b)File "/usr/local/lib/python2.7/site-packages/moderation/diff.py" in html_ta_list
61. pattern.findall(html))]
Exception Type: TypeError at /admin/moderation/moderatedobject/3/
Exception Value: expected string or buffer

Problem in diff.py with showing object differences.

Auto-approval / automoderation issues in the admin?

In the admin, as a superuser editing an instance of a model registered with django_moderation, I have to save the instance twice in order for it to take effect. I'm not using ModerationAdmin.

After the first save, the ModeratedObject edit view in the admin displays the change difference correctly, moderation_reason says "Auto-approved: Superuser", and the status is Approved, but as I said the actual model instance remains unchanged until I save it again. As I discovered later, no matter what changes I make the second time I save, only the changes from the first save are applied to the model instance after saving the second time.

I took a little time to test this a bit more, thought someone might want to know:

If I apply change A (Boolean) and change B (ForeignKey) the first time I save an existing model instance in the admin, then only change A the second time I save, both changes A and B are applied to the model instance as if I had just done the first save. In fact, as I mentioned above, it doesn't matter what changes I make before doing the second save.

As far as the ModeratedObject edit view in the admin, any change that was applied only once displays the change difference with the changes highlighted as I said. Any change applied twice consecutively just displays the new value without the highlighting.

Seems like there is some issue with auto-approval / automoderation in the admin or the way the changed objects are getting passed around? Haven't tested from shell or in views.

As an alternative to saving the object twice, if I go and approve the change in the ModeratedObject admin (despite the existing "approved" status), then the actual model instance gets updated with the saved changes. So to me it seems like maybe some part of the auto-approval process is not working correctly.

ModeratedObject matching query does not exist

I already fixed the issue, so I didn't get to copy/paste the traceback, but it happens in moderation/admin.py

def send_message(self, request, object_id):
    moderated_object = ModeratedObject.objects.get(pk=object_id)
    ...

should be:

def send_message(self, request, object_id):
    moderated_object = ModeratedObject.objects.get(object_pk=object_id)
    ...

BooleanField in moderated model

Hello,

I have a BooleanField() in a moderated model using django-moderation.
The problem is that, apparently inside class SerializedObjectField, 'True' is serialized as 1, and 'False' to 0, so that the app doesn't really work, always finding a difference between 'True' and 1, and 'False' and 0

edit: and I'm using mysql database.

Any workaround ?

Reinstate previous model instance if change approved then rejected

It would be a nice feature if, in the case that you are making a change to an existing moderated model instance, on approving the change, the previous model instance got stored in ModeratedObject.changed_object, so that if you subsequently rejected the change, that previous instance could be reinstated, just swapping changed_object for the model instance.

The moderation doesn't work

Hi everyone!
I'm trying the django-moderation app in a web system, but I can not make it work.
I have made all the steps that are explained in the documentation, but there is something wrong.
There is no errors. But when I save a moderated Model, it is directly saved in the Model's table, and not in the moderatedobject table.
I have correctly configurated the settings.py options. I have created the moderator.py file in my app's directory and putted there the registration code. I have imported and run the auto_discover script. But nothing happens.
Can someone help me?
Thank you so much!

Martin.-

Need ability for custom auto reject/approve reason.

(This relates to Issue #15)

Currently reason is tucked away inside the automoderate method of ModeratedObject and is not configurable, and in the case of custom auto moderation methods you may want to set this. In my Issue #15 example I may want to provide the reason as being "Spam" for the rejection.

The exact way to refactor this is not apparent to me at this very moment, however. But I feel that in the current code:

    if self.moderator.is_auto_reject(user):
        self.reject(moderated_by=self.moderated_by,
                     reason='Auto rejected',
                     )
    elif self.moderator.is_auto_approve(user):
        self.approve(moderated_by=self.moderated_by,
                     reason='Auto moderated',
                     )

That you could for example return a tuple from is_auto_reject of (True, 'Spam') ... This will evaluate to True, since the tuple is not empty. You will also never return (False, 'Spam') because you do not need a reason if you are not rejecting it. Same case with auto approve. Then inside the if condition you break up the tuple, assure the first of the tuple is True and then set the reason to the reason provided by the custom approve/reject method. And if there is no reason provided, or what is returned is not a tuple, use the default reason.

Of course there may be a much more elegant solution, which if you have one please do use instead. :)

ManyToMany field support

I see that it is listed as known issue. Are there any plans to integrate support for ManyToMany fields? Or do you have any thoughts or know what the issues are for implementing support? I could take a shot at it if you have any experimental code/ideas. Thanks.

RegistrationError at /adm

I have followed the steps to install and register django-moderation in my app, but I get the following errors both in the admin and frontend views:

RegistrationError at /adm

<class 'MyModelName'> has been registered with Moderation.

It seems to be saying that the model has already been registered, but I don't see how this is possible,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.