如果我们设置一个配置文件Django如何建议:
class Profile(models.Model):
user = models.ForeignKey(User, unique=True)
Run Code Online (Sandbox Code Playgroud)
然后当你User从Django admin 删除对象时,它也删除了他的配置文件.这是因为配置文件有一个外键给用户,它想保护参照完整性.但是,即使指针朝另一个方向发展,我也想要这个功能.例如,在我的Profile课上我有:
shipper = models.ForeignKey(Shipper, unique=True, blank=True, null=True)
carrier = models.ForeignKey(Carrier, unique=True, blank=True, null=True)
affiliat = models.ForeignKey(Affiliate, unique=True, blank=True, null=True, verbose_name='Affiliate')
Run Code Online (Sandbox Code Playgroud)
我想要它,以便如果你删除Profile它将删除相关的托运人/承运人/联盟对象(不要问我为什么Django做了"联盟"一些奇怪的关键字).因为托运人,承运人和附属公司是用户的类型,没有剩下的数据就没有意义存在(没有人可以作为一个人登录).
我没有把键放在其他对象上的原因是因为每当我想检查用户是哪种类型时,Django就必须在内部加入所有这些表.
kra*_*yzk 11
虽然使用上面bernardo描述的post_delete信号是一种很好的方法,但是效果很好,我尽量避免使用尽可能少的信号,因为我觉得它通过在标准功能中添加行为而不必要地使用代码可能会有所期待.
我更喜欢上面的压倒一切的方法,然而,菲利克斯给出的例子确实有一个致命的缺陷; 它覆盖的delete()函数如下所示:
def delete(self, using=None):
using = using or router.db_for_write(self.__class__, instance=self)
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
collector = Collector(using=using)
collector.collect([self])
collector.delete()
Run Code Online (Sandbox Code Playgroud)
注意参数'using',在大多数情况下我们用空参数调用delete(),所以我们甚至可能知道它在那里.在上面的示例中,此参数由我们覆盖而不是查看超类功能,如果有人在删除Profile时传递'using'参数将导致意外行为.为避免这种情况,我们会确保将参数与其默认lika一起保留:
class Profile(models.Model):
# ...
def delete(self, using=None):
if self.shipper:
self.shipper.delete()
if self.carrier:
self.carrier.delete()
if self.affiliat:
self.affiliat.delete()
super(Profile, self).delete(using)
Run Code Online (Sandbox Code Playgroud)
然而,最重要的方法的一个缺陷是,在批量删除时,每个数据库记录都不会显式调用delete(),这意味着如果您希望一次删除多个配置文件并保持覆盖行为(调用.例如,在django queryset上删除()你需要利用删除信号(如bernardo所述),否则你需要遍历每个记录单独删除它们(昂贵和丑陋).
在删除实际配置文件之前,可以覆盖delete() Profile类的方法并删除此方法中的其他对象.
就像是:
class Profile(models.Model):
# ...
def delete(self):
if self.shipper:
self.shipper.delete()
if self.carrier:
self.carrier.delete()
if self.affiliat:
self.affiliat.delete()
super(Profile, self).delete()
Run Code Online (Sandbox Code Playgroud)
更好的方法是使用对象的delete方法和queryset的delete方法使用post_delete信号,正如您在文档中看到的那样.
在您的情况下,您的代码将与此非常相似:
from django.db import models
from django.dispatch import receiver
@receiver(models.signals.post_delete, sender=Profile)
def handle_deleted_profile(sender, instance, **kwargs):
if instance.shipper:
instance.shipper.delete()
if instance.carrier:
instance.carrier.delete()
if instance.affiliat:
instance.affiliat.delete()
Run Code Online (Sandbox Code Playgroud)
这仅适用于Django 1.3或更高版本,因为post_delete此Django版本中添加了信号.
| 归档时间: |
|
| 查看次数: |
16554 次 |
| 最近记录: |