-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update access_view_set_mixin.py #75
base: master
Are you sure you want to change the base?
Conversation
Included get_queryset method to return the scope_queryset if it has been explicity declared in the access_policy. It calls super().get_queryset() instead of directly self.queryset to allow work with anothers mixins. That way, won't be necessary to call the scope_queryset everytime in the view.
At first I thought it's a good idea, but after consideration there is a downside to that, if I'm correct. Let's say we have # policies.py
class CustomAccessViewSetMixin(AccessViewSetMixin):
def get_queryset(self):
queryset = super().get_queryset()
scope_queryset = self.access_policy.scope_queryset(self.request, queryset)
if scope_queryset.exists():
return scope_queryset
return queryset
class SomeAccessPolicy(AccessPolicy):
@classmethod
def scope_queryset(cls, request, qs):
role = request.user.role
if role == "author":
return qs.filter(author=request.user)
elif role == "reviewer":
return qs.filter(reviewer=request.user)
else:
return qs.none()
# views.py
class SomeView(CustomAccessViewSetMixin, viewsets.ModelViewSet):
queryset = SomeObject.objects.all()
access_policy = SomeAccessPolicy Then if I believe that here the default approach for overwriting |
Good point, thanks! What you think about: def get_queryset(self):
queryset = super().get_queryset()
scope_queryset = self.access_policy.scope_queryset
if scope_queryset.__name__ not in self.access_policy.__dict__:
return queryset
return scope_queryset(self.request, queryset) That way, it always will return the |
I think that it will always evaluates to false by default: In [20]: from rest_access_policy import AccessPolicy
In [21]: access_policy = AccessPolicy
In [22]: scope_queryset = access_policy.scope_queryset
In [23]: scope_queryset.__name__ not in access_policy.__dict__
Out[23]: False The most stratightforward solution I see right now is to go with something like: class AccessPolicy(permissions.BasePermission):
"..."
@classmethod
def scope_queryset(cls, request, qs):
raise NotImplementedError # was: return qs.none()
class CustomAccessViewSetMixin(AccessViewSetMixin):
def get_queryset(self):
queryset = super().get_queryset()
try:
scope_queryset = self.access_policy.scope_queryset(self.request, queryset)
return scope_queryset
except NotImplementedError:
return queryset Or something in similar fashion. Raising |
def get_queryset(self):
queryset = super().get_queryset()
scope_queryset = self.access_policy.scope_queryset
if scope_queryset.__name__ not in self.access_policy.__dict__:
return queryset
return scope_queryset(self.request, queryset)
Yes, because of the from app.views_access_policies import ScopedQuerysetAccessPolicy, NotScopedQuerysetAccessPolicy
# AccessPolicy without scope_queryset:
NotScopedQuerysetAccessPolicy.scope_queryset.__name__ not in NotScopedQuerysetAccessPolicy.__dict__
# Out: True (view's queryset will be returned)
# AccessPolicy with scope_queryset:
ScopedQuerysetAccessPolicy.scope_queryset.__name__ not in ScopedQuerysetAccessPolicy.__dict__
# Out: False (scope_queryset will be returned) That way, if I'm correct, I think it will remains optional to implement or not the Sorry if I didn't understand what you said. |
It didn't occur to me that the mixin could call this automatically for the user. Thanks for the input.
I guess I'd lean toward the viewset mixin always calling scope_queryset, which seems like the safest default because it will return zero rows, unless the user defines it. @helderlgoliveira @micurbanski what do you think? |
I'm fine with that, but just to be sure- it would got something like this: class AccessPolicy(permissions.BasePermission):
"..."
@classmethod
def scope_queryset(cls, request, qs):
return qs.none()
###
class AccessViewSetMixin:
"..."
def get_queryset(self):
queryset = super().get_queryset()
return self.access_policy.scope_queryset(self.request, queryset) And the worst case scenario it will return |
yep, exactly :) |
I agree, but it'll be a breaking change. Apps that didn't override If I'm not wrong, maybe this could maintain backwards compability: def get_queryset(self):
queryset = super().get_queryset()
scope_queryset = self.access_policy.scope_queryset
if scope_queryset.__name__ not in self.access_policy.__dict__:
return queryset
return scope_queryset(self.request, queryset) Or changing the default @classmethod
def scope_queryset(cls, request, qs):
return qs # before: qs.none() Regards |
oh, good point about the breaking change. What do you think about introducing a new mixin called |
I agree, good idea |
I did a PR for that: #105 |
Included get_queryset method to return the scope_queryset if it has been explicity declared in the access_policy.
It calls super().get_queryset() instead of directly self.queryset to allow work with anothers mixins.
That way, won't be necessary to call the scope_queryset everytime in the view.