1

I'm trying to create filter for my results that would take multiple values from html form. This current setup gives me urls such as this /?language=French&language=German however the results would not show French and German records but only French. Additionally, adding new filtering criteria such as "level" /?language=French&level=Beginner doesn't work either.

Could anybody please help and point me in the right direction?

thanks sikor

My form looks as follows:

RESOURCES_LANGUAGE =        (('English', 'English'),
                            ('Spanish', 'Spanish'),
                            ('French', 'French'),
                            ('German', 'German'))

RESOURCES_LEVEL =           (('Beginner', 'Beginner'),
                            ('Intermediate', 'Intermediate'),
                            ('Advanced', 'Advanced'))

SORTBY =                    (('likes', 'Likes'),
                            ('name', 'Name'),
                            ('latest', 'Latest'))

class FiltersAndSortingForm(forms.Form): 
    language = forms.MultipleChoiceField(required=False, label='Language', widget=forms.CheckboxSelectMultiple, choices=RESOURCES_LANGUAGE)
    level = forms.MultipleChoiceField(required=False, label='Level', widget=forms.CheckboxSelectMultiple, choices=RESOURCES_LEVEL)
    provider = forms.ModelMultipleChoiceField(queryset=Provider.objects.all().order_by('name'), label='Provider', required=False,)
    sortby = forms.MultipleChoiceField(required=False, label='Sort by', widget=forms.CheckboxSelectMultiple, choices=SORTBY)

My view:

def resources(request):
    if request.GET:
        language = request.GET.get('language', '')
        level = request.GET.get('level', '')
        provider = request.GET.get('provider', '')  

        sortby = request.GET.get('sortby', '')
        if sortby == 'name':
            orderby = 'name'
        elif sortby == 'latest':
            orderby = '-dt_added'
        elif sortby == 'likes':
            orderby = '-facebook_likes'
        else:
            orderby = '-facebook_likes'    

        qset = (
            Q(language=language) 
#            & 
#            Q(level=level) 
            )

        resources = Resource.objects.filter(inactive=0).filter(qset).order_by(orderby)
        form = FiltersAndSortingForm()
    else:
        form = FiltersAndSortingForm()
        resources = Resource.objects.filter(inactive=0).order_by('-facebook_likes')
Tomasz Jakub Rup
  • 10,502
  • 7
  • 48
  • 49
sikor
  • 143
  • 2
  • 10
  • Well, `filter(Q(language="French")&Q(level="Beginner"))` absolutely should work if there is a resource thats both French and Beginner. I would remove some stuff in where you are testing and verify that a hardcoded line like that works... – Daniel Rucci May 04 '14 at 00:36
  • Also, your mention of language=french&language=german only returns french. Is that how you want it to be or is that another problem you are having? – Daniel Rucci May 04 '14 at 00:37
  • Well that is the problem. Each object have only one language in the model. Here I want to return list of objects with french and list of objects with german in one combined result but the current setup doesn't give me that. Looks like it ignores everything after the first argument – sikor May 04 '14 at 09:30

2 Answers2

1

OK, eventually after looking at this thread django dynamically filtering with q objects I got it working like this. Maybe it is not the cleanest way but seems like it is doing the job. Unless anybody could suggest better solution?

thanks -s

def resources(request):
    if request.GET:
        type = request.GET.getlist('type', '')
        language = request.GET.getlist('language', '')
        level = request.GET.getlist('level', '')
        provider = request.GET.getlist('provider', '')  

        sortby = request.GET.get('sortby', '')
        if sortby == 'name':
            orderby = 'name'
        elif sortby == 'latest':
            orderby = '-dt_added'
        elif sortby == 'likes':
            orderby = '-facebook_likes'
        else:
            orderby = '-facebook_likes'  

        qset_type = Q() # Create an empty Q object to start with
        for x in type:
            qset_type |= Q(provider__tags__name=x) # 'or' the Q objects together

        qset_language = Q() 
        for x in language:
            qset_language |= Q(language=x) 

        qset_level = Q() 
        for x in level:
            qset_level |= Q(level=x) 

        qset_provider = Q() 
        for x in provider:
            qset_provider |= Q(provider=x)    

        qset = qset_language & qset_level & qset_type & qset_provider

        resources = Resource.objects.filter(inactive=0).filter(qset).order_by(orderby)
        form = FiltersAndSortingForm()
    else:
        form = FiltersAndSortingForm()
        resources = Resource.objects.filter(inactive=0).order_by('-facebook_likes')
Community
  • 1
  • 1
sikor
  • 143
  • 2
  • 10
0

I don't see additional criteria being added to the qset, that's why filtering doesn't work on those parameters. Also I would recommend that you use a dictionary for your search criteria and then unpack it. Check this question out for a detailed explanation Django: How do I use a string as the keyword in a Q() statement?. Let me know if you need any clarification.

Community
  • 1
  • 1
  • I see what you mean, but I'm still struggling with translating it into my case exactly. Would you mind helping bit more here please ? – sikor May 04 '14 at 09:35
  • One thing that became clear to me now is that language is actually a list so I need to have `language = request.GET.getlist('language', '')` but how do I take each of those values into my qset now ? – sikor May 04 '14 at 15:40
  • Yes, that's right. The length of the list is equal to the number of selected check boxes for a particular choice field. – user3599948 May 05 '14 at 01:20