djangodjango-modelsdjango-model-field

Flagging a django model instance during save for later processing


I'm running into a problem with using a flag to mark model instances for future processing. I have a class:

class MyModel(models.Model):
    processed = models.BooleanField(default=False)
    changed = models.DateTimeField(auto_now=True)
    # More fields.

    def save(self):
        self.processed = False
        super().save(*args, **kwargs)

Then I have a management command:

class Command(BaseCommand):

    def handle(self, *args: Any, **kwargs: Any) -> None:
        models = MyModel.objects.filter(processed=False).order_by("changed")[:200]
        for model in models:
            # Do some processing
            model.processed = True
            model.save()

Now, obviously when the model is saved, it's just going to re-mark the instance as unprocessed.

I'm new to django, so my knowledge of the model lifecycle and the available methods is pretty limited. I've been reading through the documentation and haven't found any solution so far.

Any ideas on how I could tackle this?


Solution

  • Probably the best way to solve this is by adding a parameter to ignore flagging this:

    class MyModel(models.Model):
        processed = models.BooleanField(default=False)
        changed = models.DateTimeField(auto_now=True)
        # More fields.
    
        def save(self, *args, unset_processed=True, **kwargs):
            if unset_processed:
                self.processed = False
            super().save(*args, **kwargs)

    Then in your base command, we can work with:

    class Command(BaseCommand):
    
        def handle(self, *args: Any, **kwargs: Any) -> None:
            models = MyModel.objects.filter(processed=False).order_by("changed")[:200]
            for model in models:
                # Do some processing
                model.processed = True
                model.save(unset_processed=False)

    Note however that creating, updating, etc. in bulk will circumvent calling .save(…) on the models. So if you for example use:

    MyModel.objects.filter(pk__in=[1,4,2,5]).update(some_field=some_value)

    will for example not call the .save(…) method. The documentation on .update(…) [Django-doc] for example states:

    Finally, realize that update() does an update at the SQL level and, thus, does not call any save() methods on your models, nor does it emit the pre_save or post_save signals

    (…)

    If you want to update a bunch of records for a model that has a custom save() method, loop over them and call save(), like this:

    for e in Entry.objects.filter(pub_date__year=2010):
        e.comments_on = False
        e.save()