Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a Non-Default Manager with GenericForeignKey()

I have altered the default manager on some of the objects which a GenericForeignKey() can reference such that those objects may no longer appear within that default manager.

I have other managers which will be able to find these deleted objects, but I see no way to tell the content types framework about them. Is this possible?

I am implementing 'soft deletion' with some models which involves the following managers:

from django.db import models

SDManager(models.Manager):

    def get_query_set(self):
        return super(SDManager, self).get_query_set().filter(is_deleted=False)

SDDeletedManager(models.Manager):

    def get_query_set(self):
        return super(SDDeletedManager, self).get_query_set().filter(is_deleted=True)

This allows me to do the following:

SDModel(models.Model):
    # ...
    objects = SDManager() # Only non (soft) deleted objects
    all_objects = models.Manager() # The default manager
    deleted_objects = SDDeletedManager() # Only (soft) deleted objects

When using a GenericForeignKey() field in a model to reference an object defined such as SDModel, it uses the _default_manager attribute which evaluates to the objects manager, to get the reference. This means it looses references when objects are soft deleted.

This was one of the main reasons I was using GenericForeignKey() fields. A solution I have been milling over is implementing a lesser version of the content types framework, so that I can define my own get_object() which uses the all_objects manager to access the references object.

So my question really is:

Is it possible to use a non-default manager with the existing content types framework so that it finds the soft deleted objects, or will I have to re implement all the parts I need from scratch?

like image 730
Marcus Whybrow Avatar asked Nov 05 '22 15:11

Marcus Whybrow


1 Answers

I have the exact same issue as you, and after diving into the documentation/source it looks like Django does not provide an out of the box way to do this. The simplest method I found was to subclass GenericForeignKey and then override the __get__ method.

The troublesome line is where it calls:

rel_obj = ct.get_object_for_this_type(pk=getattr(instance, self.fk_field))

So this line needs to be rewritten as:

rel_obj = ct.model_class().all_objects.get(pk=getattr(instance, self.fk_field))

It's a little bit hackish but it works, and you then get to use the full power of GenericForeignKey like you usually would.

like image 85
Spike Avatar answered Nov 11 '22 06:11

Spike