27

I have a series of tests and cases in a database. Whenever a test is obsoleted, it gets end dated, and any sub-cases of that test should also be end dated. I see two ways to accomplish this:

1) Modify the save function to end date sub-cases.
2) Create a receiver which listens for Test models being saved, and then end dates their sub-cases.

Any reason to use one other than the other?

Edit: I see this blog post suggests to use the save method whenever you check given values of the model. Since I'm checking the end_date, maybe that suggests I should use a custom save?

Edit2: Also, for the record, the full hierarchy is Protocol -> Test -> Case -> Planned_Execution, and anytime one is end_dated, every child must also be endDated. I figure I'll end up doing basically the same thing for each.

Edit3: It turns out that in order to tell whether the current save() is the one that is endDating the Test, I need to have access to the old data and the new data, so I used a custom save. Here's what it looks like:

def save(self):
    """Use a custom save to end date any subCases"""
    try:
        orig = Test.objects.get(id=self.id)
        enddated = (not orig.end_date) and self.end_date is not None   
    except:
        enddated = False

    super(Test, self).save()

    if enddated:
        for case in self.case_set.exclude(end_date__isnull=False):
            case.end_date = self.end_date
            case.enddater = self.enddater
            case.save()
Nathan
  • 4,545
  • 6
  • 32
  • 49
  • 2
    I'd just add that you should probably accept *args and **kwargs in save and pass them to super in case someone wants to use params like update_save or force_insert. – ShawnFumo Aug 19 '13 at 21:14

2 Answers2

30

I generally use this rule of thumb:

  • If you have to modify data so that the save won't fail, then override save() (you don't really have another option). For example, in an app I'm working on, I have a model with a text field that has a list of choices. This interfaces with old code, and replaces an older model that had a similar text field, but with a different list of choices. The old code sometimes passes my model a choice from the older model, but there's a 1:1 mapping between choices, so in such a case I can modify the choice to the new one. Makes sense to do this in save().
  • Otherwise, if the save can proceed without intervention, I generally use a post-save signal.
mipadi
  • 398,885
  • 90
  • 523
  • 479
  • 5
    Or to be more specific - use save when you need to manipulate the object itself. This means your object's methods have fewer 'side-effects' elsewhere in your code which makes maintenance much easier. – adamnfish Apr 08 '11 at 15:40
16

In my understanding, signals are a means for decoupling modules. Since your task seems to happen in only one module I'd customize save.

jammon
  • 3,404
  • 3
  • 20
  • 29