2012-11-28 38 views
0

考慮這個models.py如何防止級聯對象的pre_delete?

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) 

我不能刪除任何環境,因爲UserEnvironment其中pre_save信號發送:

<<< [email protected]!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" 
>>> 

我也想保持這種例外,CannotRemoveLastAdmin,除了當環境正在被刪除。

可以通過粘貼安全重現此問題:

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 

並運行:

Environment.objects.all().delete() 

這僅需要運行測試,爲setUp()問題Environment.objects.all().delete(),所以我寧願沒有混亂我的模型與像marked_for_delete的額外領域...

快樂的黑客和預先感謝您的幫助。

回答

0

如果你想在你的模型上執行這樣的策略,你必須以某種方式標記環境,否則你將永遠無法刪除它。

如果您確實不喜歡marked_for_delete選項,則可能會濫用環境名稱字段(通過設置特殊值)作爲解決方法。這對我來說似乎更加惱人。