diff --git a/ippisite/ippidb/templates/compound_list.html b/ippisite/ippidb/templates/compound_list.html
index 8e1cd2cfb898bc9a4a8ed40dc22e65a678f72b9e..adc3a788fa1e325172a70b02a915bef12562edc2 100644
--- a/ippisite/ippidb/templates/compound_list.html
+++ b/ippisite/ippidb/templates/compound_list.html
@@ -30,32 +30,32 @@
        {% include "slider_button.html" with label="Publications" param_name="pubs" %}
         </div>
         <div class="m-2 d-flex ">
-            <span>{{ compounds.paginator.count }} compounds</span>
-            {% if selected_families or selected_ppis or selected_diseases or selected_taxonomies or selected_boundcomplexes or molecular_weight != molecular_weight_max or a_log_p != a_log_p_max or nb_donor_h != nb_donor_h_max or pubs != pubs_min %}
+            <span>{{ paginator.count }} compounds</span>
+            {% if selected_family or selected_ppi or selected_disease or selected_taxonomy or selected_boundcomplex or molecular_weight != molecular_weight_max or a_log_p != a_log_p_max or nb_donor_h != nb_donor_h_max or pubs != pubs_min %}
             <span>
                 &nbsp;-&nbsp;filters:&nbsp;
-                {% if selected_families %}
-                    {% for selected in selected_families %}
+                {% if selected_family %}
+                    {% for selected in selected_family %}
                         {% include "selected_badge.html" with param_name="family" selected=selected %}
                     {% endfor %}
                 {% endif %}
-                {% if selected_ppis %}
-                    {% for selected in selected_ppis %}
+                {% if selected_ppi %}
+                    {% for selected in selected_ppi %}
                         {% include "selected_badge.html" with param_name="ppi" selected=selected %}
                     {% endfor %}
                 {% endif %}
-                {% if selected_diseases %}
-                    {% for selected in selected_diseases %}
+                {% if selected_disease %}
+                    {% for selected in selected_disease %}
                         {% include "selected_badge.html" with param_name="disease" selected=selected %}
                     {% endfor %}
                 {% endif %}
-                {% if selected_taxonomies %}
-                    {% for selected in selected_taxonomies %}
+                {% if selected_taxonomy %}
+                    {% for selected in selected_taxonomy %}
                         {% include "selected_badge.html" with param_name="taxonomy" selected=selected %}
                     {% endfor %}
                 {% endif %}
-                {% if selected_boundcomplexes %}
-                    {% for selected in selected_boundcomplexes %}
+                {% if selected_boundcomplex %}
+                    {% for selected in selected_boundcomplex %}
                         {% include "selected_badge.html" with param_name="boundcomplex" selected=selected %}
                     {% endfor %}
                 {% endif %}
@@ -96,20 +96,20 @@
             </span>
         </div>
     </form>
-{% if compounds %}
+{% if page_obj %}
     <div class="container-fluid">
     {% if display == 'v' %}
         <div class="d-flex flex-wrap m-2 justify-content-around">
-        {% for compound in compounds %}
+        {% for compound in page_obj %}
             {% include "compound_v_item.html" with compound=compound %}
         {% endfor %}
         </div>
     {% elif display == 'l'%}
-        {% for compound in compounds %}
+        {% for compound in page_obj %}
             {% include "compound_l_item.html" with compound=compound %}
         {% endfor %}
     {% else %}
-        {% include "compound_t_list.html" with compounds=compounds %}
+        {% include "compound_t_list.html" with compounds=page_obj %}
     {% endif %}
     </div>
 {% else %}
@@ -121,9 +121,9 @@
 
 <nav class="d-flex">
   <ul class="pagination pagination-sm ml-auto">
-    {% if compounds.has_previous %}
+    {% if page_obj.has_previous %}
     <li class="page-item">
-      <a class="page-link" href="?{% url_replace request 'page' compounds.previous_page_number  %}" aria-label="Previous">
+      <a class="page-link" href="?{% url_replace request 'page' page_obj.previous_page_number  %}" aria-label="Previous">
         <span aria-hidden="true">&laquo;</span>
         <span class="sr-only">Previous</span>
       </a>
@@ -131,12 +131,12 @@
     {% endif %}
 
     <li class="page-item disabled">
-     <span class="page-link">Page {{ compounds.number }} of {{ compounds.paginator.num_pages }}.</span>
+     <span class="page-link">Page {{ page_obj.number }} of {{ paginator.num_pages }}.</span>
     </li>
 
-    {% if compounds.has_next %}
+    {% if page_obj.has_next %}
     <li class="page-item">
-      <a class="page-link" href="?{% url_replace request 'page' compounds.next_page_number  %}" aria-label="Next">
+      <a class="page-link" href="?{% url_replace request 'page' page_obj.next_page_number  %}" aria-label="Next">
         <span aria-hidden="true">&raquo;</span>
         <span class="sr-only">Next</span>
       </a>
@@ -145,7 +145,7 @@
 
     <li class="page-item ml-3">
       <div class="input-group input-group-sm">
-        <input id="pageNumber" type="number" min="1" max="{{compounds.paginator.num_pages}}" step="1" placeholder="page #" style="width: 4em;" aria-label="Page number">
+        <input id="pageNumber" type="number" min="1" max="{{ paginator.num_pages }}" step="1" placeholder="page #" style="width: 4em;" aria-label="Page number">
         <div class="input-group-append">
           <button class="btn btn-outline-primary" type="button" onclick="modifyUrl('page',$('#pageNumber').val())">Go</button>
         </div>
@@ -155,11 +155,11 @@
   </ul>
 </nav>
 
-  {% include "multiselection_modal.html" with label="PPI Family" selected_list=selected_families unselected_list=families param_name="family" all_param_name="families_all" all_param_value=families_all %}
-  {% include "multiselection_modal.html" with label="PPI" selected_list=selected_ppis unselected_list=ppis param_name="ppi" all_param_name="ppis_all" all_param_value=ppis_all %}
-  {% include "multiselection_modal.html" with label="Disease" selected_list=selected_diseases unselected_list=diseases param_name="disease" all_param_name="diseases_all" all_param_value=diseases_all %}
-  {% include "multiselection_modal.html" with label="Organism" selected_list=selected_taxonomies unselected_list=taxonomies param_name="taxonomy" all_param_name="taxonomies_all" all_param_value=taxonomies_all %}
-  {% include "multiselection_modal.html" with label="Bound complex" selected_list=selected_boundcomplexes unselected_list=boundcomplexes param_name="boundcomplex" all_param_name="boundcomplexes_all" all_param_value=boundcomplexes_all %}
+  {% include "multiselection_modal.html" with label="PPI Family" selected_list=selected_family unselected_list=family param_name="family" all_param_name="family_all" all_param_value=family_all %}
+  {% include "multiselection_modal.html" with label="PPI" selected_list=selected_ppi unselected_list=ppi param_name="ppi" all_param_name="ppi_all" all_param_value=ppi_all %}
+  {% include "multiselection_modal.html" with label="Disease" selected_list=selected_disease unselected_list=disease param_name="disease" all_param_name="disease_all" all_param_value=disease_all %}
+  {% include "multiselection_modal.html" with label="Organism" selected_list=selected_taxonomy unselected_list=taxonomy param_name="taxonomy" all_param_name="taxonomy_all" all_param_value=taxonomy_all %}
+  {% include "multiselection_modal.html" with label="Bound complex" selected_list=selected_boundcomplex unselected_list=boundcomplex param_name="boundcomplex" all_param_name="boundcomplex_all" all_param_value=boundcomplex_all %}
   {% include "slider_modal.html" with label="Molecular Weight" param_name="molecular_weight" param_min=molecular_weight_min param_max=molecular_weight_max param_value=molecular_weight step='50' param_label='Select a cutoff value for the molecular weight of the compounds to be selected'%}
   {% include "slider_modal.html" with label="AlogP" param_name="a_log_p" param_min=a_log_p_min param_max=a_log_p_max param_value=a_log_p step='1' param_label='Select a cutoff value for the AlogP of the compounds to be selected'%}
   {% include "slider_modal.html" with label="H donors" param_name="nb_donor_h" param_min=nb_donor_h_min param_max=nb_donor_h_max param_value=nb_donor_h step='1' param_label='Select a cutoff value for the number of H donors in the compounds to be selected'%}
diff --git a/ippisite/ippidb/urls.py b/ippisite/ippidb/urls.py
index 82df36de8cb3866c8d31301829743b07849ecdb4..db1fb11d78afd320c977e3085d935d775f9e9517 100644
--- a/ippisite/ippidb/urls.py
+++ b/ippisite/ippidb/urls.py
@@ -11,7 +11,7 @@ urlpatterns = [
     url(r'^$', views.index, name='index'),
     url(r'^about$', views.about, name='about'),
      url(r'^about/general/$', views.general, name='general'),
-    url(r'^compounds/$', views.compound_list, name='compound_list'),
+    url(r'^compounds/$', views.CompoundListView.as_view(), name='compound_listbeta'),
     url(r'^compounds/(?P<compound_id>\w+)$', views.compound_card, name='compound_card'),
     url(r'^tutorials$', views.tutorials, name='tutorials'),
     url(r'^admin-session$', views.adminSession, name='admin-session'),
diff --git a/ippisite/ippidb/views.py b/ippisite/ippidb/views.py
index 6ae892102921ae96ee47225b340e1fc09996075e..62077cb803765e9d7237703c2030916d40ef0356 100644
--- a/ippisite/ippidb/views.py
+++ b/ippisite/ippidb/views.py
@@ -7,6 +7,7 @@ from django.shortcuts import render
 from django.http import HttpResponseRedirect, Http404
 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 from django.contrib.auth.mixins import LoginRequiredMixin
+from django.views.generic.list import ListView
 from formtools.wizard.views import SessionWizardView, NamedUrlSessionWizardView
 
 import ippidb
@@ -143,99 +144,151 @@ class IppiWizard(LoginRequiredMixin, NamedUrlSessionWizardView):
         'form_data': [form.cleaned_data for form in form_list],
     })
 
+class CompoundListFilterHandler(object):
+
+    def __init__(self, filter_class, relation_from_compound, relation_to_compound, parameter_name, filter_context, request_get):
+        self.filter_class = filter_class
+        self.relation_from_compound = relation_from_compound
+        self.parameter_name = parameter_name
+        self.filter_context = filter_context
+        self.relation_from_compound = relation_from_compound
+        self.relation_to_compound = relation_to_compound
+        self.value = request_get.getlist(parameter_name)
+
+    def process(self, queryset):
+        """ to be called during queryset filtering """
+        # if a value has been set for this filter
+        if self.value:
+            # filter queryset on the ID values specified
+            queryset = queryset.filter(**{self.relation_from_compound + '__id__in': self.value})
+            # selected objects on the ID values specified
+            self.filter_context['selected_' + self.parameter_name] = self.filter_class.objects.filter(id__in = self.value)
+            # unselected objects on the ID values not specified
+            self.filter_context[self.parameter_name] = self.filter_class.objects.exclude(id__in = self.value)
+        return queryset
+    
+    def post_process(self, compound_ids, queryset):
+        """ to be called after queryset filtering """
+        # if no value has been set for this filter
+        if not self.value:
+            # selected objects is an empty queryset
+            self.filter_context['selected_' + self.parameter_name] = self.filter_class.objects.none()
+            if compound_ids:
+                # unselected objects is the range related to the current queryset
+                self.filter_context[self.parameter_name] = self.filter_class.objects.filter(**{self.relation_to_compound + '__id__in':compound_ids}).distinct()
+            else:
+                # unselected objects are all objects if no filter is set
+                self.filter_context[self.parameter_name] = self.filter_class.objects.all()
+
+
+class CompoundCutoffFilterHandler(object):
+
+    def __init__(self, parameter_name, cutoff_dir, filter_context, request_get):
+        self.parameter_name = parameter_name
+        self.cutoff_dir = cutoff_dir
+        self.filter_context = filter_context
+        self.value = request_get.get(parameter_name)
+
+    def process(self, queryset):
+        """ to be called during queryset filtering """
+        # if a value has been set for this filter
+        if self.value:
+            # filter queryset on the ID values specified
+            self.filter_context[self.parameter_name] = self.value
+            filter_dict = {self.parameter_name + '__' + self.cutoff_dir + 'te': self.value}
+            queryset = queryset.filter(**filter_dict)
+            # max and min value are the max and min for 
+            self.filter_context[self.parameter_name+'_max'] = str(int(math.ceil(float(Compound.objects.aggregate(Max(self.parameter_name))[self.parameter_name + '__max'] or 0))))
+            self.filter_context[self.parameter_name+'_min'] = str(int(math.floor(float(Compound.objects.aggregate(Min(self.parameter_name))[self.parameter_name + '__min'] or 0))))
+        return queryset
+
+    def post_process(self, compound_ids, queryset):
+        """ to be called after queryset filtering """
+        # if no value has been set for this filter
+        if not self.value:
+            self.filter_context[self.parameter_name+'_max'] = str(int(math.ceil(float(queryset.aggregate(Max(self.parameter_name))[self.parameter_name + '__max'] or 0))))
+            self.filter_context[self.parameter_name+'_min'] = str(int(math.floor(float(queryset.aggregate(Min(self.parameter_name))[self.parameter_name + '__min'] or 0))))
+            if self.cutoff_dir == 'l':
+                self.filter_context[self.parameter_name] = self.filter_context[self.parameter_name+'_max']
+            else:
+                self.filter_context[self.parameter_name] = self.filter_context[self.parameter_name+'_min']
+
+
+class CompoundListView(ListView):
+
+    model = Compound
+    template_name = "compound_list.html"
+    paginate_by = 15
+    
+    table_view_default_fields = ['id', 'common_name', 'molecular_weight', 'a_log_p', 'compound_action_pdb_ids', 'pubs']
 
-def process_cutoff_value(name, context, request, cutoff_dir='l'):
-    c = context['compounds']
-    context[name+'_max'] = str(int(math.ceil(float(c.aggregate(Max(name))[name + '__max'] or 0))))
-    context[name+'_min'] = str(int(math.floor(float(c.aggregate(Min(name))[name + '__min'] or 0))))
-    if request.GET.get(name):
-        context[name] = request.GET.get(name)
-        filter_dict = {name + '__' + cutoff_dir + 'te': context[name]}
-        context['compounds'] = context['compounds'].filter(**filter_dict)
-    elif cutoff_dir == 'l':
-        context[name] = context[name+'_max']
-    else:
-        context[name] = context[name+'_min']
-    return context
-
-def compound_list(request):
-    """
-    Display the list of compounds
-    """
-    context = {}
-    # pubs is the number of publications referring to the compound
-    compounds = Compound.objects.annotate(pubs=Count('refcompoundbiblio'))
-    context['compounds'] = compounds
-    if request.GET.get('family'):
-        context['compounds'] = context['compounds'].filter(compoundaction__ppi__family__id__in=request.GET.getlist('family'))
-    if request.GET.get('ppi'):
-        context['compounds'] = context['compounds'].filter(compoundaction__ppi__id__in=request.GET.getlist('ppi'))
-    if request.GET.get('disease'):
-        context['compounds'] = context['compounds'].filter(compoundaction__ppi__diseases__id__in=request.GET.getlist('disease'))
-    if request.GET.get('taxonomy'):
-        context['compounds'] = context['compounds'].filter(compoundaction__ppi_id__ppicomplex__complex__protein__organism__id__in=request.GET.getlist('taxonomy'))
-    if request.GET.get('boundcomplex'):
-        context['compounds'] = context['compounds'].filter(compoundaction__ppi_id__ppicomplex__complex__id__in=request.GET.getlist('boundcomplex'))
-    context = process_cutoff_value('molecular_weight', context, request)
-    context = process_cutoff_value('a_log_p', context, request)
-    context = process_cutoff_value('nb_donor_h', context, request)
-    context = process_cutoff_value('pubs', context, request, 'g')
-    selected_families = PpiFamily.objects.filter(id__in=request.GET.getlist('family'))
-    families = PpiFamily.objects.exclude(id__in=request.GET.getlist('family'))
-    selected_ppis = Ppi.objects.filter(id__in=request.GET.getlist('ppi'))
-    ppis = Ppi.objects.exclude(id__in=request.GET.getlist('ppi'))
-    selected_diseases = Disease.objects.filter(id__in=request.GET.getlist('disease'))
-    diseases = Disease.objects.exclude(id__in=request.GET.getlist('disease'))
-    selected_taxonomies = Taxonomy.objects.filter(id__in=request.GET.getlist('taxonomy'))
-    taxonomies = Taxonomy.objects.exclude(id__in=request.GET.getlist('taxonomy'))
-    selected_boundcomplexes = ProteinDomainBoundComplex.objects.filter(id__in=request.GET.getlist('boundcomplex'))
-    boundcomplexes = ProteinDomainBoundComplex.objects.exclude(id__in=request.GET.getlist('boundcomplex'))
     sort_by_option_ids = ['id', 'molecular_weight', 'a_log_p', 'nb_aromatic_sssr', 'nb_chiral_centers', 'pubs']
-    sort_by = request.GET.get('sort_by', 'id')
-    context['compounds'] = context['compounds'].order_by(sort_by)
-    context['compounds'] = context['compounds'].distinct()
-    # handle pagination in compounds list
-    paginator = Paginator(context['compounds'], 15)
-    page = request.GET.get('page')
-    try:
-        context['compounds'] = paginator.page(page)
-    except PageNotAnInteger:
-        # If page is not an integer, deliver first page.
-        context['compounds'] = paginator.page(1)
-    except EmptyPage:
-        # If page is out of range (e.g. 9999), deliver last page of results.
-        context['compounds'] = paginator.page(paginator.num_pages)
-    display = request.GET.get('display')
-    if display not in ['l', 't']:
-        display = 'v'
-    context['display']=display
-    compound_fields = {f.name: f.verbose_name for f in Compound._meta.get_fields() if not(f.is_relation)}
-    sort_by_options = OrderedDict()
-    for sort_by_option_id in sort_by_option_ids:
-        if sort_by_option_id == 'pubs':
-            name = 'Number of publications'
+
+    def get_ordering(self):
+        sort_by = self.request.GET.get('sort_by', 'id')
+        return sort_by
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        # process display configuration
+        display = self.request.GET.get('display')
+        if display not in ['l', 't']:
+            display = 'v'
+        context['display']=display
+        # displayed fields in "table" mode
+        fields = self.request.GET.getlist('fields', self.table_view_default_fields)    
+        if 'id' not in fields:
+            fields.append('id')
+        context['fields'] = fields
+        # filters
+        context.update(self.filter_context)
+        # sort by options
+        compound_fields = {f.name: f.verbose_name for f in Compound._meta.get_fields() if not(f.is_relation)}
+        sort_by_options = OrderedDict()
+        for sort_by_option_id in self.sort_by_option_ids:
+            if sort_by_option_id == 'pubs':
+                name = 'Number of publications'
+            else:
+                name = compound_fields.get(sort_by_option_id)
+            sort_by_options[sort_by_option_id] = {'name': name, 'order':'ascending', 'id': sort_by_option_id}
+            sort_by_options['-'+sort_by_option_id] = {'name': name, 'order':'descending', 'id': sort_by_option_id}
+        context['sort_by'] = self.get_ordering()
+        context['sort_by_options'] = sort_by_options
+        # return context
+        return context
+
+    def get_queryset(self):
+        self.filter_context = {}
+        # get queryset
+        qs = super().get_queryset()
+        # annotate
+        qs = qs.annotate(pubs=Count('refcompoundbiblio'))
+        # add filters
+        cfhs = [\
+            CompoundListFilterHandler(PpiFamily, 'compoundaction__ppi__family', 'ppi__compoundaction__compound', 'family', self.filter_context, self.request.GET),
+            CompoundListFilterHandler(Ppi, 'compoundaction__ppi', 'compoundaction__compound', 'ppi', self.filter_context, self.request.GET),
+            CompoundListFilterHandler(Disease, 'compoundaction__ppi__diseases', 'ppi__compoundaction__compound', 'disease', self.filter_context, self.request.GET),
+            CompoundListFilterHandler(Taxonomy, 'compoundaction__ppi_id__ppicomplex__complex__protein__organism', 'protein__proteindomaincomplex__ppicomplex__ppi__compoundaction__compound', 'taxonomy', self.filter_context, self.request.GET),
+            CompoundListFilterHandler(ProteinDomainBoundComplex, 'compoundaction__ppi_id__ppicomplex__complex', 'ppicomplex__ppi__compoundaction__compound', 'boundcomplex', self.filter_context, self.request.GET),
+            CompoundCutoffFilterHandler('molecular_weight', 'l', self.filter_context, self.request.GET),
+            CompoundCutoffFilterHandler('a_log_p', 'l', self.filter_context, self.request.GET),
+            CompoundCutoffFilterHandler('nb_donor_h', 'l', self.filter_context, self.request.GET),
+            CompoundCutoffFilterHandler('pubs', 'g', self.filter_context, self.request.GET),
+        ]
+        for cfh in cfhs:
+            qs = cfh.process(qs)
+        # sort results
+        # post filter filters
+        if self.filter_context:
+            # compound ids in the final selection if a selection has happened
+            cids = [c for c in qs.all().values_list('id', flat=True)]
         else:
-            name = compound_fields.get(sort_by_option_id)
-        sort_by_options[sort_by_option_id] = {'name': name, 'order':'ascending', 'id': sort_by_option_id}
-        sort_by_options['-'+sort_by_option_id] = {'name': name, 'order':'descending', 'id': sort_by_option_id}
-    
-    fields = request.GET.getlist('fields',['id', 'common_name', 'molecular_weight', 'a_log_p', 'compound_action_pdb_ids', 'pubs'])    
-    if 'id' not in fields:
-        fields.append('id')
-    context['selected_ppis'] = selected_ppis
-    context['ppis'] = ppis
-    context['selected_families'] = selected_families
-    context['families'] = families
-    context['selected_diseases'] = selected_diseases
-    context['diseases'] = diseases
-    context['selected_taxonomies'] = selected_taxonomies
-    context['taxonomies'] = taxonomies
-    context['selected_boundcomplexes'] = selected_boundcomplexes
-    context['boundcomplexes'] = boundcomplexes
-    context['fields'] = fields
-    context['sort_by'] = sort_by
-    context['sort_by_options'] = sort_by_options
-    return render(request, 'compound_list.html', context)
+            cids = None
+        for cfh in cfhs:
+            cfh.post_process(cids, qs)
+        # return queryset
+        return qs
+
 
 def compound_card(request, compound_id):
     try: