caldf
caldf

Reputation: 71

filtering with a for or if loop

So I'm currently working on a project where I'm trying to improve the code.

Currently, I have this as my views.py

def home1(request):
    if request.user.is_authenticated():


        location = request.GET.get('location', request.user.profile.location)
        users = User.objects.filter(profile__location=location) 
        print users

        matchesper = Match.objects.get_matches_with_percent(request.user)
        print matchesper

        matches = [match for match in matchesper if match[0] in users][:20]

Currently, users gives me back a list of user that have the same location as the request.user and matchesper gives me a match percentage with all users. Then matches uses these two lists to give me back a list of users with their match percentage and that match with the request.users location

This works perfectly, however as soon the number of users using the website increases this will become very slow? I could add [:50] at the end of matchesper for example but this means you will never match with older users that have the same location as the request.user.

My question is, is there not a way to just create matches with matchesper for only the users that have the same location? Could I use an if statement before matchesper or a for loop?

I haven't written this code but I do understand it, however when trying to improve it I get very stuck, I hope my explanation and question makes sense.

Thank you for any help in advance I'm very stuck!

Upvotes: 0

Views: 147

Answers (1)

pbaranay
pbaranay

Reputation: 585

(I'm assuming you're using the matchmaker project.)

In Django, you can chain QuerySet methods. You'll notice that the models.py file you're working from defines both a MatchQuerySet and a MatchManager. You might also notice that get_matches_with_percent is only defined on the Manager, not the QuerySet.

This is a problem, but not an insurmountable one. One way around it is to modify which QuerySet our manager method actually works on on. We can do this by creating a new method that is basically a copy of get_matches_with_percent, but with some additional filtering.

class MatchManager(models.Manager):

[...]

    def get_matches_with_percent_by_location(self, user, location=None):
        if location is None:
            location = user.profile.location
        user_a = Q(user_a__profile__location=location)
        user_b = Q(user_b__profile__location=location)
        qs = self.get_queryset().filter(user_a | user_b).matches(user).order_by('-match_decimal')
        matches = []
        for match in qs:
            if match.user_a == user:
                items_wanted = [match.user_b, match.get_percent]
                matches.append(items_wanted)
            elif match.user_b == user:
                items_wanted = [match.user_a, match.get_percent]
                matches.append(items_wanted)
            else:
                pass
        return matches

Note the use of repeated chaining in line 10! That's the magic.

Other notes:

  • Q objects are a way of doing complex queries, like multiple "OR" conditions.
  • An even better solution would factor out the elements that are common to get_matches_with_percent and get_matches_with_percent_by_location to keep the code "DRY", but this is good enough for now ;)
  • Be mindful of the fact that get_matches_with_percent returns a vanilla list instead of a Django QuerySet; it's a "terminal" method. Thus, you can't use any other QuerySet methods (like filter) after invoking get_matches_with_percent.

Upvotes: 1

Related Questions