2

How can I override the update() method of a django model inside a custom manager?

I would like to modify the behavior of some methods(all(), update(), filter()) of a django model and I have tried to override using what my code down here suggests but nothing is happening. I have tried using QuerySet instead of inheriting from Manager but I am failing to override it properly as I am getting empty results all over the system.

class undeletedObjectManager(models.Manager):
    def get_queryset(self):
        return super(undeletedObjectManager, self).get_queryset().filter(deleted=False)

    def update(self, *args, **kwargs):
        if "deleted" in args:
            # some logic here
        super().update(*args, **kwargs)

class Transaction(models.Model):
    author = models.ForeignKey(Branch, null=True)
    objects = undeletedObjectManager()    

    def __str__(self):
        return 'Tr. by {}'.format(self.author.name)

the update() method isn't reached and I am guessing it is because I am not overriding the correct member. Is there a specific method name? in both Manager and QuerySet classes? How should I override them?

Olfredos6
  • 808
  • 10
  • 19

1 Answers1

3

I think you are doing it right with the get_queryset override. You don't need to use update() override here. I think you are missing the part with attaching it to the model class:

class YourModel(models.Model):
    objects = undeletedObjectManager()

Update

I think your update override should be something like this:

def update(self, *args, **kwargs):
    if "deleted" in args:
        # some logic here
    super().update(**kwargs)

Experimental:

Can you give a try like this

def update(self, **kwargs):
    deleted = kwargs.pop('deleted', False):
    if deleted:
        # some logic here
    super().update(**kwargs)

Update 2:

from django.db.models.query import QuerySet
from django.db.models.manager import BaseManager

class CustomQueryset(QuerySet):
    def update(self, **kwargs):
        deleted = kwargs.pop('deleted', False)
        if deleted:
            # some logic here
        super().update(**kwargs)

class YourManager(BaseManager.from_queryset(CustomQueryset)):
    # rest of the code
Olfredos6
  • 808
  • 10
  • 19
ruddra
  • 50,746
  • 7
  • 78
  • 101
  • I am attaching it properly to the model class. I can tell by the fact that get_queryset is working just fine cause objects with property `deleted=True` are not being returned. What I'd like to accomplish now is adding another override for the update() method. I'm going to edit my question by adding where I do attach it to the model – Olfredos6 Dec 27 '18 at 12:12
  • @Olfredos6 please see my update section. Hope it helps – ruddra Dec 27 '18 at 12:27
  • Alright I will change that, thanks. But the problem persists; calling the update() method is still calling the default one instead of my override – Olfredos6 Dec 27 '18 at 12:32
  • I actually tested it in my local machine. it worked. Although the method signature is bit different(because the actual update has only keyword argument `**kwargs`). – ruddra Dec 27 '18 at 12:37
  • @Olfredos6 can you check my Experimental section please? – ruddra Dec 27 '18 at 12:39
  • I have checked your experimental section and even applied your suggestion. Still the method is not being called. I have tried printing to the console as soon as we enter the method but nothing is printed and the expected result isn't there. I tried to override the update() and delete() methods directly in other model classes(without going through creating another manager) it works(although in an unexpected way too ) but that approach on the Transaction model isn't working at all!! – Olfredos6 Dec 27 '18 at 12:49
  • Can you write a custom method i.e. `custom_update` but use the existing the codes from the given answer and try using that? @Olfredos6 – ruddra Dec 27 '18 at 12:55
  • I'm getting this AttributeError exception : `AttributeError: 'super' object has no attribute 'update'` – Olfredos6 Dec 27 '18 at 13:08
  • even when using self – Olfredos6 Dec 27 '18 at 13:11
  • I am starting to think that overriding update isn't allowed @ruddra – Olfredos6 Dec 27 '18 at 13:13
  • I am surprised a bit. I have tested your code with django 1.11 with your code, it was going inside update block; what django version are you using? – ruddra Dec 27 '18 at 13:17
  • The django version in my virtual env is 1.11.3. I'm going to try your update2, it looks better; will come back to you with the result. @ruddra – Olfredos6 Dec 27 '18 at 13:22
  • 1
    Worked like a charm man!! Thanks a lot for your time a patience @ruddra – Olfredos6 Dec 27 '18 at 13:34
  • 2
    By the way @ruddra please edit your code for future readers for the following errors: the import statement on Basemanager should be `from django.db.models.**manager** import BaseManager` and should also remove the **:** after `deleted = kwargs.pop('deleted', False)` – Olfredos6 Dec 27 '18 at 13:41