114

The Django docs only list examples for overriding save() and delete(). However, I'd like to define some extra processing for my models only when they are created. For anyone familiar with Rails, it would be the equivalent to creating a :before_create filter. Is this possible?

zaidfazil
  • 9,017
  • 2
  • 24
  • 47
ground5hark
  • 4,464
  • 8
  • 30
  • 35

7 Answers7

205

Overriding __init__() would cause code to be executed whenever the python representation of object is instantiated. I don't know rails, but a :before_created filter sounds to me like it's code to be executed when the object is created in the database. If you want to execute code when a new object is created in the database, you should override save(), checking if the object has a pk attribute or not. The code would look something like this:

def save(self, *args, **kwargs):
    if not self.pk:
        # This code only happens if the objects is
        # not in the database yet. Otherwise it would
        # have pk
    super(MyModel, self).save(*args, **kwargs)
Arany
  • 1,238
  • 11
  • 11
Zach
  • 18,594
  • 18
  • 59
  • 68
  • 9
    I've actually found a solution using signals: http://docs.djangoproject.com/en/dev/topics/signals/ (the pre_save signal, specifically). However, this seems to be a much more pragmatic solution. Thanks a bunch. – ground5hark Feb 24 '10 at 04:16
  • nice hack. I'd still prefer to override the create method as I'd like to add a logging.info call after creation of a new object. Calling it before and you have chance of that the the call to the parent method fail... – philgo20 Mar 25 '10 at 16:34
  • 4
    I assume you mean overriding the manager method `create`? That's an interesting solution, but it wouldn't work in cases when the object is being created using `Object(**kwargs).save()` or any other variation on that. – Zach Mar 25 '10 at 17:07
  • 5
    I don't think it's a hack. It's one of the official solutions. – skywalker Mar 07 '16 at 11:53
  • You can override the create method with a custom manager or add a classmethod on the model class. https://docs.djangoproject.com/en/1.10/ref/models/instances/#creating-objects – Ryan Allen Sep 01 '16 at 14:56
  • 7
    Shouldn't it be `super(MyModel, self).save(*args, **kwargs)` ? – Mark Chackerian Jun 14 '17 at 17:22
  • @ground5hark you can have it both *a method* **and** *using signals*, I posted below the "have the cake and eat it too" solution because this is the first thing people see when googling how to do this :) cheers! – NeuronQ Aug 11 '17 at 14:42
  • 12
    Maybe checking for `self.pk` is not the best option to check if the object is newly being created or just updating. Sometimes you provide object id in creation time (a customized non-database generated value like `KSUID`), and it'll cause this clause never to execute... There is `self._state.adding` value to make sure if it's saving for the first time or just updating, which helps in those cases. – Shahin Jun 19 '18 at 14:52
  • Thanks, Its a great solution with a single if condition... Good one@Zach. – Raviteja Jul 03 '20 at 16:57
42

To answer the question literally, the create method in a model's manager is a standard way to create new objects in Django. To override, do something like

from django.db import models

class MyModelManager(models.Manager):
    def create(self, **obj_data):
        # Do some extra stuff here on the submitted data before saving...
        # For example...
        obj_data['my_field'] = my_computed_value(obj_data['my_other_field'])

        # Now call the super method which does the actual creation
        return super().create(**obj_data) # Python 3 syntax!!

class MyModel(models.model):
    # An example model
    my_field = models.CharField(max_length=250)
    my_other_field = models.CharField(max_length=250)

    objects = MyModelManager()

In this example, I'm overriding the Manager's method create method to do some extra processing before the instance is actually created.

NOTE: Code like

my_new_instance = MyModel.objects.create(my_field='my_field value')

will execute this modified create method, but code like

my_new_unsaved_instance = MyModel(my_field='my_field value')

will not.

Mark Chackerian
  • 21,866
  • 6
  • 108
  • 99
  • I believe you have to call `super(MyModelManager, self).create(**obj_data)` instead of only `super().create(**obj_data)`. Other than that, this is a brilliant solution – Ricardo Vilaça Apr 22 '21 at 17:01
  • 4
    Like the comment says, Python 3 syntax!! – Mark Chackerian Apr 23 '21 at 13:11
  • 3
    I think this is the best solution after the "save" overload. The logic of signals is harder to follow, and does not fit the neat class-based solution of Django models. I always prefer overloading model methods or using Managers, instead of signals. – disconnectionist Aug 25 '21 at 07:18
42

This is old, has an accepted answer that works (Zach's), and a more idiomatic one too (Michael Bylstra's), but since it's still the first result on Google most people see, I think we need a more best-practices modern-django style answer here:

from django.db.models.signals import post_save

class MyModel(models.Model):
    # ...
    @classmethod
    def post_create(cls, sender, instance, created, *args, **kwargs):
        if not created:
            return
        # ...what needs to happen on create

post_save.connect(MyModel.post_create, sender=MyModel)

The point is this:

  1. use signals (read more here in the official docs)
  2. use a method for nice namespacing (if it makes sense) ...and I marked it as @classmethod instead of @staticmethod because most likely you'll end up needing to refer static class members in the code

Even cleaner would be if core Django would have an actual post_create signal. (Imho if you need to pass a boolean arg to change behavior of a method, that should be 2 methods.)

NeuronQ
  • 7,527
  • 9
  • 42
  • 60
  • 1
    Woudn't `post_init` be more appropriate? I'm new to signals, but I'd think that the way you set it up, the method is called on every `save()`, so updates would call this, too. – Matt McCarthy Jun 09 '21 at 01:00
  • 1
    Even though this is clean and I like it, please note that in Django docs it's stated: "it’s recommended to avoid the application’s root module and its models module to minimize side-effects of importing code". This can basically easily generate circular imports (it did in my case). – Kurt Bourbaki Feb 10 '22 at 13:07
  • @KurtBourbaki thanks for noticing that. please suggest a better place to put such code, preferably the simplest such place – NeuronQ Feb 14 '22 at 09:48
  • @NeuronQ I defined `post_create()` as a function inside the `signals.py` module, as recommended by Django docs. This allowed me to avoid circular imports. Unfortunately, the signal is not in the `MyModel` class, which is a pity. – Kurt Bourbaki Feb 14 '22 at 13:38
28

an example of how to create a post_save signal (from http://djangosnippets.org/snippets/500/)

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    """Create a matching profile whenever a user object is created."""
    if created: 
        profile, new = UserProfile.objects.get_or_create(user=instance)

here is a thoughtful discussion on whether it's best to use signals or custom save methods https://web.archive.org/web/20120815022107/http://www.martin-geber.com/thought/2007/10/29/django-signals-vs-custom-save-method/

In my opinion using signals for this task is more robust, easier to read but lengthier.

nnov
  • 489
  • 6
  • 14
Michael Bylstra
  • 5,042
  • 4
  • 28
  • 24
  • This is the preferred way instead of messing with object internals, however, if you make modifications to the model in question, and not just creating another in the above example, **don't forget to call `instance.save()`**. So in this case, there is also a performance penalty since there will be one INSERT and one UPDATE query to the database. – Mike Shultz Jul 11 '15 at 17:39
  • The link to the signals vs. custom save methods is broken. – Sander Vanden Hautte Apr 24 '18 at 14:44
7

Overriding __init__() will allow you to execute code when the model is instantiated. Don't forget to call the parent's __init__().

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
3

You can override the create method with a custom manager or add a classmethod on the model class. https://docs.djangoproject.com/en/1.11/ref/models/instances/#creating-objects

Ryan Allen
  • 5,414
  • 4
  • 26
  • 33
1

The preferred answer is correct but the test to tell whether the object is being created doesn't work if your model derives from UUIDModel. The pk field will already have a value.

In this case, you can do this:

already_created = MyModel.objects.filter(pk=self.pk).exists()