22

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.

Sign up to request clarification or add additional context in comments.

6 Comments

In what way is it a bad idea to override __init__? It presents no more issues in django than in any other situation.
vartec I know it's been a while, but I just bumped into your reply and I'm curious. Why do you say that signals is the Django way? Why not one of the two ways described in the Django documentation to solve this precise situation? docs.djangoproject.com/en/1.4/ref/models/instances/… Thanks
The updated link from the previous comment is this: docs.djangoproject.com/en/1.9/ref/models/instances
I would not recommend using signals for this purpose. You can get the same result by overriding the save() method, but signals are usually harder to maintain and debug. See also this article on when to use signals and when not: lincolnloop.com/blog/django-anti-patterns-signals
|
9

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

Comments

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 Comment

Is there a rule of the thumb to know which method is more appropriate than the other one depending on the context?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.