Django 连接临时 pre_save 信号

0 投票
2 回答
573 浏览
提问于 2025-04-17 21:46

我最近在处理Django信号的问题,已经纠结了好几天,希望能听听大家的想法。

场景:

我想使用一个黑箱方法(其实是geodjango中的LayerMapping加载器,但这不是重点)来生成并保存模型实例。出于某种原因,它无法设置我模型中的一个字段。很不幸的是,这个字段不能为null,所以这个黑箱方法就因为完整性错误(IntegrityError)而失败了。示例代码:

models.py

class MyModel(models.Model):
    field1 = models.IntegerField()
    field2 = models.IntegerField()
    field3 = models.IntegerField() # This one is the problem

call_black_box.py

from some_app import black_box
from models import MyModel

def call_it():
    black_box(MyModel, some_other_args) # Fails

这个黑箱程序试图创建一些MyModel的实例,但会因为错误而失败:IntegrityError: null value in column "field3" violates not-null constraint

我的解决方案:

我动态生成了一个pre_save回调,并把它附加到MyModel上,最后又把它断开(因为我不一定总是想要这种行为):

call_black_box.py

from some_app import black_box
from models import MyModel
from django.db.models.signals import pre_save

def call_it():
    def pre_save_callback(sender, instance, *args, **kwargs):
        instance.field3 = 1
    pre_save.connect(pre_save_callback, sender=MyModel)
    try:
        black_box(MyModel, some_other_args) # OK
    finally:
        pre_save.disconnect(pre_save_callback, sender=MyModel)

这样是有效的,但有没有更好的方法呢?我猜如果在这个函数执行的同时有其他操作,也许会出问题。对我来说现在没问题,因为我都是顺序执行,但这也不是最理想的做法?

谢谢!

编辑:

我没有提供足够的细节。赋值给field3的值在第二个函数中会有所不同:

def call_it_differently():
    def pre_save_callback(sender, instance, *args, **kwargs):
        instance.field3 = some_other_value
    pre_save.connect(pre_save_callback, sender=MyModel)
    try:
        black_box(MyModel, some_other_args) # OK
    finally:
        pre_save.disconnect(pre_save_callback, sender=MyModel)

所以我不能给一个默认值,也不能重写保存方法——因为这些选项只能让我指定一个单一的值,而我需要灵活性来应用任意的值。

2 个回答

0

Django信号主要用作钩子点。比如说,你正在写一个第三方应用,这个应用会被其他人使用,你想给他们一些可以操作你代码的钩子点,这时候就可以使用信号。

在你的情况下,我更倾向于重写模型的save()方法,而不是使用信号。

class MyModel(models.Model):
    field1 = models.IntegerField()
    ///
    field3 = models.IntegerField()

    def save(self, *args, **kwargs):
        self.field3 = 1
        super(MyModel, self).save(*args, **kwargs)
1

如果我理解你的问题没错的话,你可以为你模型中的第三个字段设置一个默认值。

class MyModel(models.Model):
    field1 = models.IntegerField()
    field2 = models.IntegerField()
    field3 = models.IntegerField(default=1) # This one is the problem

这样如果没有指定这个值,Django 就会默认使用 1 作为这个字段的值。

撰写回答