toothywalrus
toothywalrus

Reputation: 129

Django, how to update model in save() method after calling a super()?

I have such problem. For example, there is a field in my model class:

periodic_task = models.OneToOneField(PeriodicTask, null=True, blank=True)

I need to override the save() method to set this field value:

def save(self, *args, **kwargs):
    super(PostTweetSet, self).save(*args, **kwargs)
    self.periodic_task = TaskScheduler.create(
        'tweets.tasks.post_next_tweet', self.interval.period,
        self.interval.every, args="[" + '"%s"' % str(self.pk) + "]")

You see that self.periodic_task gets assigned after actually calling the super() method. I did so, because of need to have a pk field (I use it in TaskScheduler create method). On the other side, I need to update db-table for this model after setting this new field. If I call super() once again, I'll get error about duplicating id. So, what can I do to make this work? Or I need to reconstruct my approach for this task at all? Thanks.

Upvotes: 1

Views: 10763

Answers (5)

Wade Williams
Wade Williams

Reputation: 4140

If you don't want to use a signal, why not store the result of super() and return it instead of the call itself?

def save(self, *args, **kwargs):
    run_task = False
    if not self.pk:
        run_task = True

    result = super().save(*args, **kwargs)

    if run_task:
        TaskScheduler.create(...)

    return result

Upvotes: 0

JeffS
JeffS

Reputation: 2737

You may want to do this in the post_save signal instead.

Upvotes: 3

Dipak
Dipak

Reputation: 6950

By providing some condition in the override save method in model I have achieved same requirement.

In the question, requirement is to update the periodic_task field after saving the model, so here is the sudo code structure I have followed:

class MyModel(models.Model):
    periodic_task = models.OneToOneField(PeriodicTask, null=True, blank=True)
    def save(self):
        super(Asset, self).save()
        if not self.periodic_task: # Checking if the section is not updated.
            self.periodic_task = #code to update the field.
            self.save() #again call the save.

Upvotes: 4

Joel Burton
Joel Burton

Reputation: 1516

I'd do it with a post-save signal.

If you can't or that's otherwise unappealing, you think about your requirement as two paths:

  • if you don't the primary key, you need to save it first and get the pk
  • if you already know the primary key, you can create your periodic task and then call super

So something like (pseudocode):

def save(...):
    if self.pk is None:
        super(...)
        self.save(...)    # Call ourselves -- but this time, we'll have a primary key!
    else:
        ... create your periodic task
        super(...)

Upvotes: 7

Aaron Lelevier
Aaron Lelevier

Reputation: 20800

You can call super() at the end of your save() override. Here is an example using the slugify() method. The slugify method turns a string into a slug by converting all characters to lowercase and spaces to dashes. Here's the code:

from django.template.defaultfilters import slugify

def save(self, *args, **kwargs):
    self.slug = slugify(self.title)
    super(Product, self).save(*args, **kwargs)

Upvotes: 0

Related Questions