在Django模型字段中自定义`on_delete` param函数

时间:2017-12-21 04:24:42

标签: python django

我有一个IPv4Manage模型,其中有一个vlanedipv4network字段:

class IPv4Manage(models.Model):
    ...
    vlanedipv4network = models.ForeignKey(
        to=VlanedIPv4Network, related_name="ipv4s", on_delete=models.xxx, null=True)

我们知道,在on_delete param上,我们会填写models.xxx,例如models.CASCADE

是否可以定制一个功能,填充那里?我想在那里做其他逻辑事情。

4 个答案:

答案 0 :(得分:1)

CASCADE和PROTECT等实际上是函数,因此您应该能够在那里注入自己的逻辑。但是,需要对代码进行一定程度的检查才能确定如何获得您正在寻找的效果。

根据您的想法,它可能相对容易,例如PROTECT功能只会引发异常:

def PROTECT(collector, field, sub_objs, using):
    raise ProtectedError(
        "Cannot delete some instances of model '%s' because they are "
        "referenced through a protected foreign key: '%s.%s'" % (
            field.remote_field.model.__name__, sub_objs[0].__class__.__name__, field.name
        ),
        sub_objs
    )

但是如果你想要更复杂的东西,你必须要了解collector正在做什么,这当然是可以发现的。

请参阅source for django.db.models.deletion开始使用。

答案 1 :(得分:1)

可以在on_delete

中找到django/db/models/deletion.py的选项

例如,models.SET_NULL实现为:

def SET_NULL(collector, field, sub_objs, using):
    collector.add_field_update(field, None, sub_objs)

而models.CASCADE(稍微复杂一点)实现为:

def CASCADE(collector, field, sub_objs, using):
    collector.collect(sub_objs, source=field.remote_field.model,
                      source_attr=field.name, nullable=field.null)
    if field.null and not connections[using].features.can_defer_constraint_checks:
        collector.add_field_update(field, None, sub_objs)

所以,如果你弄清楚那些参数是什么,那么你应该能够定义自己的函数来传递给模型字段的on_delete参数。 collector很可能是Collector的一个实例(在同一个文件中定义,不确定它到底是什么),field很可能是要删除的模型字段,{ {1}}可能是该字段与对象相关的实例,sub_objs表示正在使用的数据库。

还有替代自定义逻辑用于删除,如果覆盖on_delete对你来说可能有点矫枉过正。

post_deletepre_delete允许您定义一些自定义逻辑,以便在删除实例之前或之后运行。

using

最后you can override the delete() method of the Model/Queryset但请注意使用此方法批量删除的注意事项:

  

批量操作不会调用重写的模型方法

     

请注意,在使用QuerySet批量删除对象或作为级联删除的结果时,不一定要调用对象的delete()方法。为确保执行自定义删除逻辑,您可以使用pre_delete和/或post_delete信号。

答案 2 :(得分:1)

另一个有用的解决方案是使用models.SET()您可以传递函数的地方(下例中的deleted_guest)

  

guest = models.ForeignKey('Guest',on_delete = models.SET(deleted_guest))

并且函数deleted_guest是

DELETED_GUEST_EMAIL = 'deleted-guest@introtravel.com'
def deleted_guest():
    """ used for setting the guest field of a booking when guest is deleted """
    from intro.models import Guest
    from django.conf import settings
    deleted_guest, created = Guest.objects.get_or_create(
        first_name='Deleted',
        last_name='Guest',
        country=settings.COUNTRIES_FIRST[0],
        email=DELETED_GUEST_EMAIL, 
        gender='M')
    return deleted_guest

您无法发送任何参数,您必须小心循环导入。在我的情况下,我只是设置一个填充记录,因此父模型有一个预定义的来宾代表一个已被删除的来宾。使用新的GDPR规则,我们必须能够删除访客信息。

答案 3 :(得分:0)

没有什么可以阻止你添加你自己的逻辑。但是,您需要考虑多种因素,包括与您正在使用的数据库的兼容性。

对于大多数用例,如果数据库设计正确,开箱即用的逻辑就足够了。请在此处查看您的可用选项https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey.on_delete