I’m trying to be a little clever and make my querysets more reusable. I have a long list of endpoints where I have objects related to either a case or a visit. In the example below, I demonstrate a queryset which returns objects of a particular model as defined in a view. The purpose of this queryset is to limit the objects in the queryset based on the access to the underlying objects that a user should or should not have.
def visit_related_queryset(view): """ We want to find all the objects which are related to the user by way of `case` and the user's relation to it. e.g., did the user create the case, are they a contributor or are they a uni admin and should therefore see all of a uni's objects + any other objects belonging to a case to which they have contributed. :param view: The DRF view :return: A queryset of the object defined in the view """ user = view.request.user uni = Q(visit__case__contributing_uni=user.university) contributor = Q(visit__case__contributors__contributor__user=user) case_creator = Q(visit__case__creator__user=user) permitted_users = Q(contributor | case_creator) # for regular users admin = Q(contributor | case_creator | uni) # for uni admin staff visit = Q(visit__id=view.kwargs["visit"]) if is_eclinic_admin(user): return view.model.objects.all() if is_uni_admin(user): return view.model.objects.all().filter(visit, admin) return view.model.objects.all().filter(visit, permitted_users)
This queryset works well, and after moving to it from similarly custom querysets under each view where the visit’s UUID is used as the filter, all tests continue to pass. That’s a relief!
In addition to the visit related views, I have many views that don’t directly related to a visit, but do so indirectly via a FK relationship.
As an example, a
question object has this relationship to
answer object this relationship to
I’m trying to find a way whereby I can reuse my
def visit_related_queryset(view) method to filter queries for endpoints which do not have a direct relationship to
Visit. At first I thought I could use
**kwargs but I came unstuck when trying to workout how I would unpack
**kwargs into the correct
Additionally, I’m trying to work out how I can pass a string as an argument
kwarg, e.g., I was thinking I could pass a variable into
Q to achieve something like this: `Q(my_filter=my_value). This could be as much as a lack of Python specific knowledge as it is a lack of Django specific knowledge.
I’m playing around with the above two thoughts trying to find an elegant solution and avoiding have to write custom queryset methods with only very minor filtering variations. I thought it a good idea to raise my hand here whilst playing around and before I get myself in a pickle.
I should mention what I am currently doing to make my code a bit more reusable. I have first made the following queryset:
def get_visit_relation_queryset(view, uni, contributor, creator): user = view.request.user permitted_users = Q(contributor | creator) admin_staff = Q(contributor | creator | uni) visit = Q(visit__id=view.kwargs["visit"]) if is_eclinic_admin(user): return view.model.objects.all() if is_uni_admin(user): return view.model.objects.all().filter(visit, admin_staff) return view.model.objects.all().filter(visit, permitted_users)
and then depending on the view, I do something like this:
def get_queryset(self): user = self.request.user uni = Q(history__visit__case__contributing_uni=user.university) contributor = Q(history__visit__case__contributors__contributor__user=user) creator = Q(history__visit__case__creator__user=user) return querysets.get_visit_relation_queryset(self, uni, contributor, creator).select_related("visit")
As always folks, thank you for your input and help.