import six
from voluptuous import Invalid
from rest_framework.exceptions import ParseError

from .schema import base_query_params_schema


class FiltersMixin(object):
    '''
    This viewset provides dynamically generated
    filters by applying defined filters on generic
    queryset.
    '''

    def __get_queryset_filters(self, query_params, *args, **kwargs):
        '''
        get url_params and query_params and make db_filters
        to filter the queryset to the finest.
        [1] ~ sign is used to negated / exclude a filter.
        [2] when a CSV is passed as value to a query params make a filter
            with 'IN' query.
        '''

        filters = []
        excludes = []

        if getattr(self, 'filter_mappings', None) and query_params:
            filter_mappings = self.filter_mappings

            try:
                # check and raise 400_BAD_REQUEST for invalid query params
                filter_validation_schema = getattr(
                    self,
                    'filter_validation_schema',
                    base_query_params_schema
                )
                query_params = filter_validation_schema(query_params)
            except Invalid as inst:
                raise ParseError(detail=inst)

            iterable_query_params = (
                query_params.iteritems() if six.PY2 else query_params.items()
            )

            for query, value in iterable_query_params:
                # [1] ~ sign is used to exclude a filter.
                is_exclude = '~' in query
                if query in self.filter_mappings and value:
                    query = filter_mappings[query]
                    # [2] multiple options is filter values will execute as `IN` query
                    if isinstance(value, list):
                        query += '__in'
                    if value:
                        if is_exclude:
                            excludes.append((query, value))
                        else:
                            filters.append((query, value))

        return dict(filters), dict(excludes)

    def __merge_query_params(self, url_params, query_params):
        '''
        merges the url_params dict with query_params query dict and returns
        the merged dict.
        '''
        url_params = {}
        for key in query_params:
            url_params[key] = query_params.get(key) # get method on query-dict works differently than on dict.
        return url_params

    def get_db_filters(self, url_params, query_params):
        '''
        returns a dict with db_filters and db_excludes values which can be
        used to apply on viewsets querysets.
        '''

        # merge url and query params
        query_params = self.__merge_query_params(url_params, query_params)

        # get queryset filters
        db_filters = self.__get_queryset_filters(query_params)[0]
        db_excludes = self.__get_queryset_filters(query_params)[1]

        return {
            'db_filters': db_filters,
            'db_excludes': db_excludes,
        }

@@ -813,16 +813,102 @@
 mappings
+%0A            value_transformations = getattr(self, 'filter_value_transformations', %7B%7D)
 %0A%0A      
@@ -1706,16 +1706,23 @@
    query
+_filter
  = filte
@@ -1739,16 +1739,167 @@
 %5Bquery%5D%0A
+                    transform_value = value_transformations.get(query, lambda val: val)%0A                    transformed_value = transform_value(value)%0A
         
@@ -2023,16 +2023,54 @@
 e, list)
+ and not query_filter.endswith('__in')
 :%0A      
@@ -2092,16 +2092,23 @@
    query
+_filter
  += '__i
@@ -2114,42 +2114,8 @@
 in'%0A
-                    if value:%0A    
     
@@ -2145,20 +2145,16 @@
 xclude:%0A
-    
         
@@ -2191,26 +2191,45 @@
 d((query
-, 
+_filter, transformed_
 value))%0A
         
@@ -2220,20 +2220,16 @@
 value))%0A
-    
         
@@ -2266,28 +2266,24 @@
             
-    
 filters.appe
@@ -2287,26 +2287,45 @@
 ppend((query
-, 
+_filter, transformed_
 value))%0A%0A   

Add value transformation logic