django-safedelete icon indicating copy to clipboard operation
django-safedelete copied to clipboard

Declaring a manager with a custom queryset does not work.

Open ddahan opened this issue 6 years ago • 6 comments

When following the SafeDelete documentation, it seems declaring a manager with a custom queryset should be done like this: objects = SafeDeleteManager(MyCustomQuerySet)

Following this:

  • The manager works as expected
  • But the methods of my custom queryset are not visible! I got errors like: AttributeError: 'SafeDeleteManager' object has no attribute 'my_custom_method_from_my_custom_queryset' when I try to access it.

I found a workaround using from_queryset method with safedelete? objects = SafeDeleteManager.from_queryset(MyCustomQuerySet)()

This way, everything seems to work, but I'm worry I'm missing something. Could you please tell me if I did something wrong with the official method? Otherwise, the documentation should be updated.

Note : I'm using Django 1.11 Thanks a lot for this very useful lib by the way.

ddahan avatar Apr 20 '18 10:04 ddahan

I think your solution should be the correct one. I see that we overwrite _queryset_class in our __init__ method, but it is always better to keep the same api as Django.

I don't remember why it was done that way, we should try to remove the __init__ method and change the documentation accordingly if this works as intended.

Thanks for the report :+1: .

Gagaro avatar Apr 24 '18 13:04 Gagaro

I don't think objects = SafeDeleteManager.from_queryset(MyCustomQuerySet)() is a workaround as much as it is recommended way of using custom Managers and QuerySets, it's documented at https://docs.djangoproject.com/en/1.10/ref/models/querysets/#django.db.models.query.QuerySet.as_manager.

For advanced usage you might want both a custom Manager and a custom QuerySet. You can do that by calling Manager.from_queryset() which returns a subclass of your base Manager with a copy of the custom QuerySet methods

class BaseManager(models.Manager):
    def manager_only_method(self):
        return

class CustomQuerySet(models.QuerySet):
    def manager_and_queryset_method(self):
        return

class MyModel(models.Model):
    objects = BaseManager.from_queryset(CustomQuerySet)()
CustomManager = BaseManager.from_queryset(CustomQuerySet)

class MyModel(models.Model):
    objects = CustomManager()

aljp avatar Jul 31 '18 04:07 aljp

I am getting the error while extending SafeDeleteQueryset.

I have a queryset like

class MyQuerySet(SafeDeleteQueryset):
    def active(self):
        return self.filter(is_active=True)

and the manager be like

class MyManager(SafeDeleteManager):

    def get_queryset(self):
        return MyQuerySet(self.model, using=self._db)

    def active(self):
        return self.get_queryset().active()

and using the custom manager in model like

class MyModel(SafeDeleteModel):
   objects = MyManager()

This is giving error

'MyQuerySet' object has no attribute '_safedelete_visibility'

Updated the manager with _safedelete_visibility = DELETED_INVISIBLE but deleted objects are still showing in the filter queryset.

anuj9196 avatar Dec 30 '19 12:12 anuj9196

You are overriding the get_safedelete method which set the _safedelete_visibility attribute. See https://github.com/makinacorpus/django-safedelete/blob/master/safedelete/managers.py#L57-L62.

Set _queryset_class on your manager to MyQuerySet and remove your get_queryset method.

Gagaro avatar Dec 31 '19 02:12 Gagaro

You are overriding the get_queryset method of the manager that is where _safedelete_visibility is set. As. @Gagaro points you have two solitions:

class MyQuerySet(SafeDeleteQueryset):
    def active(self):
        return self.filter(is_active=True)

class MyManager(SafeDeleteManager):
   _queryset_class = MyQuerySet
    def active(self):
        return self.get_queryset().active()
        
class MyModel(SafeDeleteModel):
   objects = MyManager()

class MyQuerySet(SafeDeleteQueryset):
    def active(self):
        return self.filter(is_active=True)

class MyManager(SafeDeleteManager):
    def active(self):
        return self.get_queryset().active()
        
class MyModel(SafeDeleteModel):
   objects = MyManager(MyQuerySet)

Both worked for me

gonzaloamadio avatar Nov 29 '21 17:11 gonzaloamadio

class MyModel(SafeDeleteModel):
    objects = SafeDeleteManager.from_queryset(MyQuerySet)(queryset_class=MyQuerySet)

Works pretty well too.

sashis avatar Jun 22 '22 04:06 sashis