Advertisement
Guest User

Untitled

a guest
Jun 7th, 2017
51
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.02 KB | None | 0 0
  1. import six
  2. from django import template
  3. import collections
  4.  
  5. from django.http import QueryDict
  6. from django.utils.encoding import force_text
  7.  
  8. register = template.Library()
  9.  
  10.  
  11. @register.simple_tag
  12. def build_query(**kwargs):
  13.     """Build a query string"""
  14.     query_dict = QueryDict(mutable=True)
  15.  
  16.     for k, v in kwargs.items():
  17.         if isinstance(v, collections.Iterable) and not isinstance(v, six.string_types):
  18.             query_dict.setlist(k, v)
  19.         else:
  20.             query_dict[k] = v
  21.  
  22.     return query_dict.urlencode()
  23.  
  24.  
  25. @register.simple_tag(takes_context=True)
  26. def modify_query(context, **kwargs):
  27.     """
  28.    Modify the current query.
  29.  
  30.    The following actions are available:
  31.        - ``set``: Overwrites everything for a key. `None` clears the key completely. Cannot be used with append or remove.
  32.        - ``append``: Append to the list of a key. `None` is an invalid value.
  33.        - ``remove``: Remove all occurrences of a specific value for a key. `None` is an invalid value.
  34.  
  35.    Actions are applied by adding multiple key/value arguments defining the action and the query key in the key, and
  36.    the value(s) for the query in the value.
  37.  
  38.    Actions are applied in the order ``remove``, ``append``, ``set``.
  39.  
  40.    Iterables are supported for all actions. ``{% modify_query append_test=1 append_test=2 %}``, is equivalent to
  41.    ``{% modify_query append_test=my_list %}`` if ``my_list`` is the list defined by ``[1, 2]``.
  42.  
  43.    :param context:
  44.    :param kwargs: Key/value pairs defining modifications to the query. The key must have the form ``[action]_[key]``.
  45.    :return: A query string
  46.    """
  47.  
  48.     def validate_action(k):
  49.         if '_' not in k:
  50.             return False
  51.         if k.startswith('set_') or k.startswith('append_') or k.startswith('remove_'):
  52.             if k.count('_') == 1 and k.endswith('_'):
  53.                 return False
  54.             return True
  55.         return False
  56.  
  57.     invalid_keys = {key for key in kwargs if not validate_action(key)}
  58.     for k in kwargs:
  59.         if not '_' in k:
  60.             raise ValueError('The following keys does not define a valid action: {0}'.format(', '.join(invalid_keys)))
  61.  
  62.     set_actions = dict()
  63.     append_actions = dict()
  64.     remove_actions = dict()
  65.  
  66.     # Build action dicts
  67.     for k, value in kwargs.items():
  68.         action, key = k.split('_', 1)
  69.  
  70.         # Set
  71.         if action == 'set':
  72.             if key in set_actions:
  73.                 raise ValueError(
  74.                     'A key can only have one "set" action. The argument "{0}" has been defined more than once'.format(k)
  75.                 )
  76.             if key in append_actions or key in remove_actions:
  77.                 raise ValueError(
  78.                     'A key cannot have both a "set" action and "append" or "remove" actions.'
  79.                     ' The argument "{0}" violates this constraint since "append" and/or "remove" actions also exists'
  80.                     ' for the key "{1}".'.format(k, key)
  81.                 )
  82.  
  83.             value_list = []
  84.  
  85.             if value is not None:
  86.                 if isinstance(value, collections.Iterable) and not isinstance(value, six.string_types):
  87.                     for value in value:
  88.                         value_list.append(force_text(value))
  89.                 else:
  90.                     value_list.append(force_text(value))
  91.  
  92.             set_actions[key] = value_list
  93.  
  94.         # Append
  95.         if action == 'append':
  96.             if key in set_actions:
  97.                 raise ValueError(
  98.                     'A key cannot have both a "set" action and "append" or "remove" actions.'
  99.                     ' The argument "{0}" violates this constraint since a set action also exists'
  100.                     ' for the key "{1}".'.format(k, key)
  101.                 )
  102.             if value is None:
  103.                 raise ValueError(
  104.                     'None is not a permitted append action value.'
  105.                     ' The argument "{0}" has a None value.'.format(k)
  106.                 )
  107.  
  108.             if key not in append_actions:
  109.                 append_actions[key] = []
  110.  
  111.             if isinstance(value, collections.Iterable) and not isinstance(value, six.string_types):
  112.                 for value in value:
  113.                     append_actions[key].append(force_text(value))
  114.             else:
  115.                 append_actions[key].append(force_text(value))
  116.  
  117.         # Remove
  118.         if action == 'remove':
  119.             if key in set_actions:
  120.                 raise ValueError(
  121.                     'A key cannot have both a "set" action and "append" or "remove" actions.'
  122.                     ' The argument "{0}" violates this constraint since a set action also exists'
  123.                     ' for the key "{1}".'.format(k, key)
  124.                 )
  125.             if value is None:
  126.                 raise ValueError(
  127.                     'None is not a permitted remove action value.'
  128.                     ' The argument "{0}" has a None value.'.format(k)
  129.                 )
  130.  
  131.             if key not in remove_actions:
  132.                 remove_actions[key] = []
  133.  
  134.             if isinstance(value, collections.Iterable) and not isinstance(value, six.string_types):
  135.                 for value in value:
  136.                     remove_actions[key].append(force_text(value))
  137.             else:
  138.                 remove_actions[key].append(force_text(value))
  139.  
  140.  
  141.     # Apply changes
  142.     query_dict = context.request.GET.copy()
  143.  
  144.     # Remove
  145.     for key, value_list in remove_actions.items():
  146.         # Only remove if the key is actually in the current query
  147.         if key in query_dict:
  148.             for value in value_list:
  149.                 # Remove as long as the value is present. A key can have the same value multiple times.
  150.                 while value in query_dict.getlist(key):
  151.                     query_dict.getlist(key).remove(value)
  152.  
  153.     # Append
  154.     for key, value_list in append_actions.items():
  155.         for value in value_list:
  156.             query_dict.appendlist(key, value)
  157.  
  158.     # Set
  159.     for key, value_list in set_actions.items():
  160.         query_dict.setlist(key, value_list)
  161.  
  162.     # Return
  163.     return query_dict.urlencode()
  164.  
  165.  
  166. @register.simple_tag(takes_context=True)
  167. def set_query_values(context, **kwargs):
  168.     """Override existing parameters in the current query string"""
  169.     query_dict = context.request.GET.copy()
  170.  
  171.     for k, v in kwargs.items():
  172.         if isinstance(v, collections.Iterable) and not isinstance(v, six.string_types):
  173.             query_dict.setlist(k, v)
  174.         else:
  175.             query_dict[k] = v
  176.  
  177.     return query_dict.urlencode()
  178.  
  179.  
  180. @register.simple_tag(takes_context=True)
  181. def append_query_values(context, **kwargs):
  182.     """Append to existing parameters in the current query string"""
  183.     query_dict = context.request.GET.copy()
  184.  
  185.     for k, v in kwargs.items():
  186.         if isinstance(v, collections.Iterable) and not isinstance(v, six.string_types):
  187.             for v_item in v:
  188.                 query_dict.appendlist(k, v_item)
  189.         else:
  190.             query_dict.appendlist(k, v)
  191.  
  192.     return query_dict.urlencode()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement