简体   繁体   中英

How to prevent pre_delete for a cascade object?

Consider this models.py :

from django.db import models
from django.db.models import signals


class CannotRemoveLastAdmin(Exception):
    def __init__(self, env):
        super(CannotRemoveLastAdmin, self).__init__(
            u'Cannot remove the last admin of environment "%s"' % env)


class Environment(models.Model):
    name = models.CharField(max_length=100, unique=True)
    users = models.ManyToManyField('auth.user', through='UserEnvironment')

    def __unicode__(self):
        return self.name


class UserEnvironment(models.Model):
    environment = models.ForeignKey('Environment')
    user = models.ForeignKey('auth.user')
    is_admin = models.BooleanField()
    default = models.BooleanField()
    creation_datetime = models.DateTimeField(auto_now_add=True)


def admin_required_delete(sender, instance, **kwargs):
    if not instance.is_admin:
        return

    admins = UserEnvironment.objects.filter(environment=instance.environment,
        is_admin=True).exclude(pk=instance.pk).count()

    if not admins:
        raise CannotRemoveLastAdmin(instance)
signals.pre_delete.connect(admin_required_delete, sender=UserEnvironment)

I can't delete any environment because the UserEnvironment pre_save signal is sent:

<<< jpic@zen!7953 G:delete_with_m2m_through jpic_so_env
>>> ./manage.py shell_plus
From 'admin' autoload: LogEntry
From 'auth' autoload: Permission, Group, User
From 'contenttypes' autoload: ContentType
From 'sessions' autoload: Session
From 'sites' autoload: Site
From 'account' autoload: Account, SignupCode, SignupCodeResult, EmailAddress, EmailConfirmation, AccountDeletion
From 'profiles' autoload: Profile
From 'test_app' autoload: Environment, UserEnvironment
Python 2.7.3 (default, Apr 24 2012, 00:06:13) 
[GCC 4.7.0 20120414 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> Environment.objects.all().delete()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/tmp/jpic_so_env/lib/python2.7/site-packages/django/db/models/query.py", line 514, in delete
    collector.delete()
  File "/tmp/jpic_so_env/lib/python2.7/site-packages/django/db/models/deletion.py", line 61, in decorated
    func(self, *args, **kwargs)
  File "/tmp/jpic_so_env/lib/python2.7/site-packages/django/db/models/deletion.py", line 239, in delete
    sender=model, instance=obj, using=self.using
  File "/tmp/jpic_so_env/lib/python2.7/site-packages/django/dispatch/dispatcher.py", line 172, in send
    response = receiver(signal=self, sender=sender, **named)
  File "/tmp/so_test_project/test_app/models.py", line 35, in admin_required_delete
    raise CannotRemoveLastAdmin(instance)
CannotRemoveLastAdmin: Cannot remove the last admin of environment "UserEnvironment object"
>>> 

I do want to keep this exception, CannotRemoveLastAdmin, except when an Environment is being deleted.

You can reproduce this problem safely by pasting:

cd /tmp
rm -rf jpic_so_env jpic_so_test_project
virtualenv jpic_so_env
source jpic_so_env/bin/activate
git clone https://github.com/jpic/django_test.git -b delete_with_m2m_through jpic_so_test_project
cd so_test_project
git checkout delete_with_m2m_through
pip install django
pip install -r requirements.txt
./manage.py shell_plus

And run:

Environment.objects.all().delete()

This is only required for the tests to run, as setUp() issues Environment.objects.all().delete() , so I'd rather not clutter my models with an extra field like marked_for_delete ...

Happy hacking and thanks in advance for your help.

If you want to enforce such a policy on your models, you have to somehow mark the environment, since you will never be able to delete one otherwise.

If you really dont like the marked_for_delete option, you may abuse the environments name field (by settings a special value) as a workaround. Which seems more hackish to me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM