Search code examples
djangodjango-rest-frameworkdjango-filter

Django api filter search on other models without a direct foreign field


I have two models named user, skill, and profile. I am trying to implement a search filter on the user's skills. which means when someone searches for something that is contained in the skills of a user, that user would appear in the search result.

Note: when the user signs up, a signal is used to auto-create a profile for that user. The user simply updates their profile to add skills and other things.

user model

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=254, unique=True)
    name = models.CharField(max_length=250)
    picture = models.TextField(null=True, blank=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    last_login = models.DateTimeField(null=True, blank=True)
    date_joined = models.DateTimeField(auto_now_add=True)
    slug = models.SlugField(max_length=255, unique=True, blank=True)

profile model

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profiles')
    date_of_birth = models.DateField(blank=True, verbose_name="DOB", null=True)
    bio = models.TextField(max_length=500, blank=True, null=True)
    skills = models.ManyToManyField(Skill, related_name='skills')
    sex = models.CharField(max_length=6, choices=SEX, blank=True, null=True)
    type_of_body = models.CharField(max_length=8, choices=BODYTYPE, blank=True, null=True)
    feet = models.PositiveIntegerField(blank=True, null=True)
    inches = models.PositiveIntegerField(blank=True, null=True)
    lives_in = models.CharField(max_length=50, blank=True, null=True)
    updated_on = models.DateTimeField(auto_now=True)

skill model

class Skill(models.Model):
    name = models.CharField(max_length=60)
    subcategory = models.CharField(max_length=60, blank=True, null=True)
    description = models.TextField(null=True, blank=True)
    created_on = models.DateTimeField(auto_now=True)
    updated_on = models.DateTimeField(auto_now_add=True)
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.DO_NOTHING)

the user view, where the search is done from

class ListUsersView(generics.ListAPIView):
    '''
    Gets all the users in the database
    '''
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [AllowAny]
    filter_backends = [filtr.SearchFilter]
    search_fields = ['email', 'name']

currently, the solution above works, but when I add to the search_fields other fields like profiles__skills in order to include results where there is a skill like that created by ay user, the code doesn't work.

Please, how can I get the skills in the profile of a user to show in the search?


Solution

  • The SearchFilter class supports simple single query parameter based searching. The search_fields attribute should be a list of names of text type fields on the model.

    profiles__skills is not a field. You should use a text field eg. profiles__skills__name

    class ListUsersView(generics.ListAPIView):
        '''
        Gets all the users in the database
        '''
        queryset = User.objects.all()
        serializer_class = UserSerializer
        permission_classes = [AllowAny]
        filter_backends = [filtr.SearchFilter]
        search_fields = ['email', 'name', 'profiles__skills__name']