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

Suggestion: ContentTypes framework/GenericForeignKey and support Q-objects for look up value?

Open charleswhchan opened this issue 1 year ago • 1 comments

Background: We have a few models that uses Django's ContentTypes framework and GenericForeignKey https://docs.djangoproject.com/en/5.0/ref/contrib/contenttypes/

As the typical example for ScopeManager is to passed in a fixed string (below), we have difficulty using this package.

objects = ScopedManager(site='post__site')

One idea is to extend django-scopes to support Q-objects, something along the lines of:

def ScopedManager(_manager_class=models.Manager, **scopes):
    required_scopes = set(scopes.keys())

    class Manager(_manager_class):
        def __init__(self):
            super().__init__()

        def get_queryset(self):
            current_scope = get_scope()
            if not current_scope.get('_enabled', True):
                return super().get_queryset()
            missing_scopes = required_scopes - set(current_scope.keys())
            if missing_scopes:
                return DisabledQuerySet(self.model, using=self._db, missing_scopes=missing_scopes)
            else:
                filter_args = []                                                        # <-----
                filter_kwargs = {}
                for dimension in required_scopes:
                    current_value = current_scope[dimension]
                    if isinstance(current_value, Q):                                    # <-----
                        filter_args.append(current_value)                               # <-----
                    if isinstance(current_value, (list, tuple)):
                        filter_kwargs[scopes[dimension] + '__in'] = current_value
                    elif current_value is not None:
                        filter_kwargs[scopes[dimension]] = current_value
                return super().get_queryset().filter(*filter_args, **filter_kwargs)     # <-----

        def all(self):
            a = super().all()
            if isinstance(a, DisabledQuerySet):
                a = a.all()
            return a

    return Manager()

So that we can write something such as:

objects = ScopedManager(site=(Q(site=value) | Q(site=another_value))

Open to thoughts and comments, or other solutions. Thanks in advance! :)

charleswhchan avatar Feb 12 '24 19:02 charleswhchan

I would not be opposed to this kind of feature, but I'd be very cautious of using it in production, it sounds like a possible performance hazard.

raphaelm avatar Feb 15 '24 08:02 raphaelm