20

I want to do an extra initalization whenever instances of a specific django model are created. I know that overriding __init__ can lead to trouble. What other alternatives should I consider?

Update. Additional details: The intent is to initialize a state-machine that the instances of that model represent. This state-machine is provided by an imported library, and it's inner state is persisted by my django-model. The idea is that whenever the model is loaded, the state machine would be automatically initialized with the model's data.

4
  • You've misinterpreted that question. It's not that overriding __init__ doesn't work, the OP just did it all wrong. You're free to override __init__, but the efficacy of that is entirely dependent on what exactly you're trying to do. So why don't you tell us that, so we can actually help you. Commented Feb 23, 2012 at 15:33
  • Thanks, I've reread the other thread. You mean, if I keep the signature (self, *args, **kwargs), overriding __init__ won't get on django's way? Commented Feb 23, 2012 at 15:48
  • I've added some more info to the question to clarify the intent. Commented Feb 23, 2012 at 15:48
  • Does this answer your question? Writing a __init__ function to be used in django model Commented Apr 13, 2020 at 17:23

3 Answers 3

25

Overriding __init__ might work, but it's bad idea and it's not the Django way.

The proper way of doing it in Django is using signals.

The ones that are of interest to you in this case are pre_init and post_init.

django.db.models.signals.pre_init

Whenever you instantiate a Django model, this signal is sent at the beginning of the model’s __init__() method.

django.db.models.signals.post_init

Like pre_init, but this one is sent when the __init__(): method finishes

So your code should be something like

from django.db import models
from django.db.models.signals import post_init

class MyModel(models.Model):
  # normal model definition...

def extraInitForMyModel(**kwargs):
   instance = kwargs.get('instance')
   do_whatever_you_need_with(instance)

post_init.connect(extraInitForMyModel, MyModel)

You can as well connect signals to Django's predefined models.

6
7

While I agree that there often is a better approach than overriding the __init__ for what you want to do, it is possible and there might be cases where it could be useful.

Here is an example on how to correctly override the __init__ method of a model without interfering with Django's internal logic:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # add your own logic

5

The two suggested methods in the docs rely on the instance being created in an arbitrary way:

  1. Add a classmethod on the model class:

    from django.db import models
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        @classmethod
        def create(cls, title):
            book = cls(title=title)
            # do something with the book
            return book
    
    book = Book.create("Pride and Prejudice")
    
  2. Add a method on a custom manager:

    class BookManager(models.Manager):
        def create_book(self, title):
            book = self.create(title=title)
            # do something with the book
            return book
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        objects = BookManager()
    
    book = Book.objects.create_book("Pride and Prejudice")
    

If that is your case, I would go that way. If not, I would stick to @vartec's answer.

1
  • 1
    Is there a rule of the thumb to know which method is more appropriate than the other one depending on the context?
    – perror
    Commented May 1, 2020 at 10:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.