简体   繁体   中英

Django connect temporary pre_save signal

I've been struggling with a Django signal issue for a few days now and would appreciate your thoughts.

Scenario :

I wish to use a black box method (it's actually the LayerMapping loader in geodjango, but that's not important here) that generates and saves model instances. For some reason, it cannot set one of the fields in my model. Unfortunately, that field cannot be null, so the black box method fails with an IntegrityError. Example code:

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

The black box routine tries to create some instances of MyModel, but will fail with the error: IntegrityError: null value in column "field3" violates not-null constraint

My solution :

I have generated a pre_save callback dynamically, attached it to MyModel, then disconnected it at the end (because I don't necessarily always want this behaviour):

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)

It works, but is there a nicer way? I'm guessing this could go wrong if some other actions were executed at the same time as this function. Not a problem for me as I currently do everything sequentially, but not ideal?

Thanks!

edit :

I didn't give enough detail. The value assigned to field3 will be different for a second function:

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)

So I can't give a default value, or override the save method - because these options only allow me to specify a single value, whereas I need the flexibility to apply an arbitrary one.

If I understood your question correctly, you can define the default value for the third field in your model

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

So if the value is not specified then Django will take 1 as default

Django signals are mostly used as a hook point. Say you are writing a third party app which will be used by other people and you want to provide them with some hook points to manipulate your code, then signals would be used.

In your scenario, I would rather override the save() method of Model than use a signal.

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

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

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