Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import six
- from django import template
- import collections
- from django.http import QueryDict
- from django.utils.encoding import force_text
- """
- This paste is related to https://code.djangoproject.com/ticket/10941
- """
- register = template.Library()
- @register.simple_tag
- def build_query(**kwargs):
- """Build a query string"""
- query_dict = QueryDict(mutable=True)
- for k, v in kwargs.items():
- if isinstance(v, collections.Iterable) and not isinstance(v, six.string_types):
- query_dict.setlist(k, v)
- else:
- query_dict[k] = v
- return query_dict.urlencode()
- @register.simple_tag(takes_context=True)
- def modify_query(context, **kwargs):
- """
- Modify the current query.
- The following actions are available:
- - ``set``: Overwrites everything for a key. `None` clears the key completely. Cannot be used with append or remove.
- - ``append``: Append to the list of a key. `None` is an invalid value.
- - ``remove``: Remove all occurrences of a specific value for a key. `None` is an invalid value.
- Actions are applied by adding multiple key/value arguments defining the action and the query key in the key, and
- the value(s) for the query in the value.
- Actions are applied in the order ``remove``, ``append``, ``set``.
- Iterables are supported for all actions. ``{% modify_query append_test=1 append_test=2 %}``, is equivalent to
- ``{% modify_query append_test=my_list %}`` if ``my_list`` is the list defined by ``[1, 2]``.
- :param context:
- :param kwargs: Key/value pairs defining modifications to the query. The key must have the form ``[action]_[key]``.
- :return: A query string
- """
- def validate_action(k):
- if '_' not in k:
- return False
- if k.startswith('set_') or k.startswith('append_') or k.startswith('remove_'):
- if k.count('_') == 1 and k.endswith('_'):
- return False
- return True
- return False
- invalid_keys = {key for key in kwargs if not validate_action(key)}
- for k in kwargs:
- if '_' not in k:
- raise ValueError('The following keys does not define a valid action: {0}'.format(', '.join(invalid_keys)))
- set_actions = dict()
- append_actions = dict()
- remove_actions = dict()
- # Build action dicts
- for k, value in kwargs.items():
- action, key = k.split('_', 1)
- # Set
- if action == 'set':
- if key in set_actions:
- raise ValueError(
- 'A key can only have one "set" action. The argument "{0}" has been defined more than once'.format(k)
- )
- if key in append_actions or key in remove_actions:
- raise ValueError(
- 'A key cannot have both a "set" action and "append" or "remove" actions.'
- ' The argument "{0}" violates this constraint since "append" and/or "remove" actions also exists'
- ' for the key "{1}".'.format(k, key)
- )
- value_list = []
- if value is not None:
- if isinstance(value, collections.Iterable) and not isinstance(value, six.string_types):
- for v in value:
- value_list.append(force_text(v))
- else:
- value_list.append(force_text(value))
- set_actions[key] = value_list
- # Append
- if action == 'append':
- if key in set_actions:
- raise ValueError(
- 'A key cannot have both a "set" action and "append" or "remove" actions.'
- ' The argument "{0}" violates this constraint since a set action also exists'
- ' for the key "{1}".'.format(k, key)
- )
- if value is None:
- raise ValueError(
- 'None is not a permitted append action value.'
- ' The argument "{0}" has a None value.'.format(k)
- )
- if key not in append_actions:
- append_actions[key] = []
- if isinstance(value, collections.Iterable) and not isinstance(value, six.string_types):
- for value in value:
- append_actions[key].append(force_text(value))
- else:
- append_actions[key].append(force_text(value))
- # Remove
- if action == 'remove':
- if key in set_actions:
- raise ValueError(
- 'A key cannot have both a "set" action and "append" or "remove" actions.'
- ' The argument "{0}" violates this constraint since a set action also exists'
- ' for the key "{1}".'.format(k, key)
- )
- if value is None:
- raise ValueError(
- 'None is not a permitted remove action value.'
- ' The argument "{0}" has a None value.'.format(k)
- )
- if key not in remove_actions:
- remove_actions[key] = []
- if isinstance(value, collections.Iterable) and not isinstance(value, six.string_types):
- for value in value:
- remove_actions[key].append(force_text(value))
- else:
- remove_actions[key].append(force_text(value))
- # Apply changes
- query_dict = context.request.GET.copy()
- # Remove
- for key, value_list in remove_actions.items():
- # Only remove if the key is actually in the current query
- if key in query_dict:
- for value in value_list:
- # Remove as long as the value is present. A key can have the same value multiple times.
- while value in query_dict.getlist(key):
- query_dict.getlist(key).remove(value)
- # Append
- for key, value_list in append_actions.items():
- for value in value_list:
- query_dict.appendlist(key, value)
- # Set
- for key, value_list in set_actions.items():
- query_dict.setlist(key, value_list)
- # Return
- return query_dict.urlencode()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement