Skip to content
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

Consider nested filters in the extra_kwargs #48

Merged
merged 2 commits into from
Oct 26, 2020

Conversation

pablodiazgutierrez
Copy link
Contributor

Allow nested filters to be applied to the search results. So if we get {"account__admin__id": 123}, we compare the appropriate nested field. Right now, it understands "account__admin__id"` to be a field name, which is incorrect.

Discussed in issue #47 and tested successfully in my local setup.

@logston
Copy link
Collaborator

logston commented Oct 24, 2020

This is great! Thanks! Can I ask you to update the CHANGES.txt in the root of the repo and then I'll cut a release.

@logston logston self-requested a review October 24, 2020 21:10
@pablodiazgutierrez
Copy link
Contributor Author

This is great! Thanks! Can I ask you to update the CHANGES.txt in the root of the repo and then I'll cut a release.

Should be ready. Let me know if you run into any hiccups. Thanks for considering it!

@logston logston self-requested a review October 25, 2020 19:00
@logston logston merged commit 4393fe8 into 15five:master Oct 26, 2020
@logston
Copy link
Collaborator

logston commented Oct 26, 2020

@pablodiazgutierrez Looks like tests are failing. Can you take a look?

@pablodiazgutierrez
Copy link
Contributor Author

@pablodiazgutierrez Looks like tests are failing. Can you take a look?

Good catch. I think the PR in #52 should solve it. But I'm not sure how to run the tests locally. Do you have a tip to point a newbie in the right direction?

@logston
Copy link
Collaborator

logston commented Oct 28, 2020

👍 Yeah, is usually use tox from the project root and that runs the tests for all versions of Python, this requires multiple versions of python installed though. If you don't have multiple versions available, python setup.py test will do the trick.

@pablodiazgutierrez
Copy link
Contributor Author

pablodiazgutierrez commented Oct 28, 2020 via email

@logston
Copy link
Collaborator

logston commented Oct 28, 2020

This has been released as 0.16.4.

@William-Wildridge
Copy link

The get_nested_field() function is a really nice addition to this library, but doesn't seem to work for reverse relationships.

I have the same use case as Pablo (needing to filter by company in a multitennant SAAS). However in my use case the object relationship direction of the object is a reverse one, (Example Model below).

I'm using this line in get_extra_filter_kwargs:

def get_extra_filter_kwargs(request, *args, **kwargs):
            return {
                'companies__company': request.user.memberships.first().company,
            }

Similar to Pablo's original issue, for requests to /Users?filter=userName+eq+"example_username" the result is blank, but /Users/1 and Users?attributes=userName do work correctly, and apply the company restrictions as expected.

I think I've diagnosed that the hasattr() line (shown below) that was added in this merge is causing this the filtering to stop prematurely.

            if not hasattr(obj, field_name):
                return None

Example Model

class UserInCompany(models.Model):
        company = models.ForeignKey(Company, related_name = 'company_members', on_delete=models.CASCADE,  null=True, blank=True)
	user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = 'memberships', null=True, blank=True)
class Company(models.Model):
        company_name = models.CharField(max_length=200, null=True, blank=True)

@logston
Copy link
Collaborator

logston commented Jan 27, 2023

Hi @William-Wildridge, those kwargs are used here

extra_filter_kwargs = self.get_extra_filter_kwargs(self.request, uuid)
extra_filter_kwargs[self.lookup_field] = uuid
# No use of get_extra_exclude_kwargs here since we are
# searching for a specific single object.
try:
obj = self.model_cls.objects.get(**extra_filter_kwargs)
for single user gets (ie. /User/1)

and here

extra_filter_kwargs = self.get_extra_filter_kwargs(request)
qs = self._filter_raw_queryset_with_extra_filter_kwargs(qs, extra_filter_kwargs)
for searches (ie. /Users?filter=userName+eq+"example_username").

The search logic leads here

value, found = self._get_nested_field(obj, attr_key)
and then to the code you mentioned here
if not hasattr(obj, field_name):
return None, False
.

Since your user object (obj) does not have a companies attribute, the look up fails and the _get_nested_field method returns early.

Unfortunately I don't have time to update this code to handle the reverse relationships. But you could try to filter with a different set of args and add a method to your user class that would return the company manually. Eg.

def get_extra_filter_kwargs(request, *args, **kwargs):
    return {
        'company': request.user.memberships.first().company,
    }
class User:

    @property
    def company(self):
        return self.memberships.first().company

It's hacky IMO, but it could work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants