Skip to content Skip to sidebar Skip to footer

Subclass A Django Manyrelatedmanager A.k.a. Manytomanyfield

Is there a way to Subclass a Django ManyRelatedManager a.k.a. ManyToManyField ? The goal is to pre-filter all related models when calling the ManyRelatedManager by a flag of delete

Solution 1:

You can create proxy model of SelfRefrencingModel and override the default manager. Then use this proxy model in ManyToManyField.

Subclassing ManyToManyField will not help you because for the resulting queryset ManyRelatedManger is responsible.

Proxy model approach:

from django.db import models

classA(models.Model):
    children = models.ManyToManyField('AProxy')
    name = models.TextField()
    deleted = models.NullBooleanField(null=True)


classFilterDeletedManager(models.Manager):
    defget_queryset(self):
        qs = super(FilterDeletedManager, self).get_query_set()
        return qs.filter(deleted__isnull=True)


classAProxy(A):
    objects = FilterDeletedManager()
    classMeta:
        proxy = True

Caveat with this approach is that now django expects AProxy instances for children field.

So maybe better readable and maintainable approach will be to add another attribute in __init__.

from django.db import models

classA(models.Model):
    children = models.ManyToManyField('self')
    name = models.TextField()
    deleted = models.NullBooleanField(null=True)

    def__init__(self, *args, **kwargs):
        super(A, self).__init__(*args, **kwargs)
        self.deleted_null_children = self.children.filter(deleted__isnull=True)

Solution 2:

Here is my solution. @beezz, you may be correct to use a Proxy Model to do this, but I haven't used a Proxy Model before for this pattern, so this is how I solved this:

classSelfRefrencingQuerySet(models.query.QuerySet):
    passclassSelfRefrencingManager(BaseManager):

    defget_queryset(self):
        return SelfRefrencingQuerySet(self.model, self._db).filter(
            deleted__isnull=True)

classSelfRefrencingBaseModel(models.Model):

    children = models.ManyToManyField('self', blank=True, symmetrical=False,
        related_name='parents')

    # Manager
    objects = SelfRefrencingManager()
    objects_all = models.Manager()     # So you still have acccess to the# default Manager

Solution 3:

If your intention is to use this with Django Admin or ModelForm; you don't need to subclass the ManyToManyField. See the django documentation

classSelfRefrencingModel(models.Model):

    children = models.ManyToManyField('self', blank=True, symmetrical=False, 
        related_name='parents', limit_choices_to={'deleted': False}))

Note: If deleted is a BooleanField it has to be True or False. It can't be None/NULL.

beezz's idea of using a Proxy Model is also a good one.

What I sometimes do is to customise the default manager
classMyModelManager(models.Manager):
    use_for_related_fields = Truedefget_queryset(self):
        qs = super(MyModelManager, self).get_queryset()
        return qs.filter(deleted=False)


classMyModelManager(models.Model):
    objects = MyModelManager()
    _objects = models.Manger()

    deleted = models.BooleanField(default=False)

By default deleted objects will be hidden, but if need them in your queryset, you can use _objects.

Post a Comment for "Subclass A Django Manyrelatedmanager A.k.a. Manytomanyfield"