├── django_example ├── __init__.py ├── urls.py ├── views.py ├── wsgi.py ├── templates │ └── home.html └── settings.py ├── query_parameters ├── __init__.py ├── templatetags │ ├── __init__.py │ └── query_parameters.py └── tests.py ├── MANIFEST.in ├── .gitignore ├── manage.py ├── CHANGES.txt ├── setup.py ├── LICENSE └── README.rst /django_example/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /query_parameters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /query_parameters/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include CHANGES.txt 3 | include README.rst 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.pot 3 | *.pyc 4 | local_settings.py 5 | venv 6 | MANIFEST 7 | dist 8 | -------------------------------------------------------------------------------- /django_example/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import TestView 3 | 4 | urlpatterns = [ 5 | path('', TestView.as_view()), 6 | ] 7 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_example.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /django_example/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import TemplateView 2 | 3 | 4 | class TestView(TemplateView): 5 | template_name = 'home.html' 6 | 7 | def get_context_data(self, **kwargs): 8 | context = super().get_context_data(**kwargs) 9 | context['simple_variable'] = 'hello world' 10 | context['dict_variable'] = {'hello': 'world'} 11 | return context 12 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | v0.1.0, 2014-03-26 -- Initial release. 2 | v0.1.1, 2014-03-26 -- Improved documentation. 3 | v0.1.2, 2014-03-26 -- Clarified documentation. 4 | v0.1.3, 2014-03-26 -- Bug fix and unit tests 5 | v0.2.0, 2014-03-26 -- Added ability to load and store input/outputs with context variables 6 | v0.2.1, 2014-03-26 -- Robustified handling of keys with illegal variable names 7 | v0.2.2, 2015-07-21 -- Templatetags no longer attempt to replace key arguments with matching context variables 8 | -------------------------------------------------------------------------------- /django_example/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_example project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_example.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='django-query-parameters', 5 | version='0.2.3', 6 | author='Jean-Marc Skopek', 7 | author_email='jean-marc@skopek.ca', 8 | packages=['query_parameters','query_parameters.templatetags'], 9 | scripts=[], 10 | url='https://github.com/jskopek/django-query-parameters', 11 | license='LICENSE', 12 | description='Django templatetags to simplify creating, updating, and removing query parameters from querystring', 13 | long_description=open('README.rst').read(), 14 | install_requires=['Django >= 1.4'] 15 | ) 16 | 17 | 18 | -------------------------------------------------------------------------------- /django_example/templates/home.html: -------------------------------------------------------------------------------- 1 | {% load query_parameters %} 2 | 3 |
overriding query1,query2,query3 querystring values:
{% set_query_parameters query1=example_value query2='ᛖᚴ ᚷᛖᛏ ᛖᛏᛁ ᚧ ᚷᛚᛖᚱ ᛘᚾ ᚦᛖᛋᛋ ᚨᚧ ᚡᛖ ᚱᚧᚨ ᛋᚨᚱ' query3=no %}
8 | deleting query1,query2 querystring values:
{% del_query_parameters query1 query2 %}
9 | overriding query1 with simple value:
{% set_query_parameters query1=simple_variable %}
10 | overriding query1 with dict value:
{% set_query_parameters query1=dict_variable.hello %}
11 | setting query5 to query4 querystring value:
{% set_query_parameters query5=request.GET.query4 %}
12 | {% endwith %}
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 jskopek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/django_example/settings.py:
--------------------------------------------------------------------------------
1 | import os
2 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
3 |
4 | ## SECURITY WARNING: keep the secret key used in production secret!
5 | SECRET_KEY = '#2nxx4w+zut-=v6696%1d(9x^!$9dr_+jz%$me91q+dcke5pv!'
6 |
7 | ## SECURITY WARNING: don't run with debug turned on in production!
8 | DEBUG = True
9 |
10 | ## Application definition
11 | INSTALLED_APPS = (
12 | 'django.contrib.auth',
13 | 'django.contrib.contenttypes',
14 | 'django.contrib.sessions',
15 | 'django.contrib.messages',
16 | 'django_example',
17 | 'query_parameters'
18 | )
19 |
20 | ROOT_URLCONF = 'django_example.urls'
21 | TEMPLATES = [
22 | {
23 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
24 | 'DIRS': [os.path.join(BASE_DIR, 'django_example', 'templates')],
25 | 'APP_DIRS': True,
26 | 'OPTIONS': {
27 | 'context_processors': [
28 | 'django.template.context_processors.debug',
29 | 'django.template.context_processors.request',
30 | 'django.contrib.auth.context_processors.auth',
31 | 'django.contrib.messages.context_processors.messages',
32 | ]
33 | }
34 | },
35 | ]
36 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | django-query-parameters
2 | =======================
3 |
4 | Adds two template tags that simplify the manipulation of GET parameters on a querystring. Allows easy addition, manipulation, and deletion of parameters onto an existing querystring.
5 |
6 | The module is comprised of two template tags: ``set_query_parameters`` and ``del_query_parameters``.
7 |
8 | set_query_parameters
9 | --------------------
10 |
11 | Takes a 1+ list of ``key=value`` pairs and generates an updated querystring that includes those pairs. If a key does not already exist in the querystring, it will be added. If a key exists, it will be updated with the new value. For example::
12 |
13 | # current page is http://localhost/?page=1&limit=20
14 | {% load query_parameters %}
15 | ...
16 | # => ...
17 |
18 | del_query_parameters
19 | --------------------
20 |
21 | Takes a 1+ list of keys and generates an updated querystring that removes those keys. If a key does not exist in the query string, it will be ignored. For example::
22 |
23 | # current page is http://localhost/?page=1&limit=20
24 | {% load query_parameters %}
25 | ...
26 | # => ...
27 |
28 |
29 |
30 | Installing
31 | ==========
32 |
33 | Installing is a simple as running an ``easy_install`` or ``pip install`` command::
34 |
35 | pip install django-query-parameters
36 |
37 | Include the project in the ``INSTALLED_APPS`` list in your project's ``settings.py`` file::
38 |
39 | INSTALLED_APPS = (..., 'query_parameters', ...)
40 |
41 |
42 |
43 | Storing the output in the template context
44 | ==========================================
45 |
46 | By default, both template tags output the result directly. If an optional ``as`` key is specified, the results will be stored in a context variable instead. For example::
47 |
48 | {% set_query_parameters page=2 as=next_page %}
49 | Next Page
50 |
51 | {% del_query_parameters color size as=reset_filters %}
52 | Reset Filters
53 |
54 | The ``as`` key may be modified in Django's ``settings.py`` file::
55 |
56 | QUERY_PARAMETERS_VARIABLE_OUTPUT_KEY = 'output'
57 |
58 | ...
59 |
60 | {% set_query_parameters color=red output=next_page %}
61 |
62 |
63 |
64 | Using a user defined query string
65 | =================================
66 |
67 | By default, both template tags pull the query string from the request context. If an optional ``with`` key is specified, the query string will be loaded from the context variable instead. For example::
68 |
69 | {% set_query_parameters color=red as=filter %}
70 | {% set_query_parameters size=large with=filter as=modified_filter %}
71 | Apply Filter
72 |
73 | The ``with`` key may be modified in Django's ``settings.py`` file::
74 |
75 | QUERY_PARAMETERS_VARIABLE_INPUT_KEY = 'input'
76 |
77 | ...
78 |
79 | {% set_query_parameters color=red input=custom_querystring %}
80 |
--------------------------------------------------------------------------------
/query_parameters/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.core.urlresolvers import reverse
3 | from django.template import Template
4 | from django.template import Context
5 | from django.template import RequestContext
6 | from django.test.client import Client
7 | from django.http import HttpRequest
8 | from django.http import QueryDict
9 |
10 | class SetQueryParametersTestCase(TestCase):
11 | def test_empty_querystring(self):
12 | result = self.template_generator(
13 | query_string='',
14 | set_query_parameters_value='prop1=val1'
15 | )
16 | self.assertEqual(result, 'prop1=val1')
17 |
18 | def test_add_nothing(self):
19 | result = self.template_generator(
20 | query_string='prop2=val2',
21 | set_query_parameters_value=''
22 | )
23 | self.assertEqual(result, 'prop2=val2')
24 |
25 | def test_new_property(self):
26 | result = self.template_generator(
27 | query_string='prop2=val2',
28 | set_query_parameters_value='prop1=val1'
29 | )
30 | self.assertEqual(result, 'prop1=val1&prop2=val2')
31 |
32 | def test_invalid_parameters(self):
33 | from django.template import TemplateSyntaxError
34 | self.assertRaises(TemplateSyntaxError, Template, '{% load query_parameters %}{% set_query_parameters prop1 %}')
35 |
36 | def test_multiple_new_properties(self):
37 | result = self.template_generator(
38 | query_string='prop3=val3',
39 | set_query_parameters_value='prop1=val1 prop2=val2'
40 | )
41 | self.assertEqual(result, 'prop1=val1&prop2=val2&prop3=val3')
42 |
43 | def test_illegal_variable_name(self):
44 | result = self.template_generator(
45 | query_string='',
46 | set_query_parameters_value='_prop1=val1'
47 | )
48 | self.assertEqual(result, '_prop1=val1')
49 |
50 |
51 | def test_update_property(self):
52 | result = self.template_generator(
53 | query_string='prop1=val2',
54 | set_query_parameters_value='prop1=val1'
55 | )
56 | self.assertEqual(result, 'prop1=val1')
57 |
58 | def test_update_and_add_property(self):
59 | result = self.template_generator(
60 | query_string='prop1=val1&prop2=val2',
61 | set_query_parameters_value='prop1=val1_modified prop3=val3'
62 | )
63 | self.assertEqual(result, 'prop1=val1_modified&prop2=val2&prop3=val3')
64 |
65 | def test_save_in_context(self):
66 | t = Template(
67 | '{% load query_parameters %}'
68 | '{% set_query_parameters prop1=val1 as=result %}'
69 | )
70 | request = HttpRequest()
71 | request.GET = QueryDict('prop2=val2')
72 |
73 | c = RequestContext(request)
74 | result = t.render(c)
75 | self.assertEqual(result, '')
76 | self.assertEqual(c['result'], 'prop1=val1&prop2=val2')
77 |
78 | def test_load_from_context(self):
79 | t = Template(
80 | '{% load query_parameters %}'
81 | '{% set_query_parameters prop1=val1 with=existing_querystring %}'
82 | )
83 | request = HttpRequest()
84 | request.GET = QueryDict('prop2=val2')
85 |
86 | c = RequestContext(request, {'existing_querystring':'prop3=val3'})
87 | result = t.render(c)
88 | self.assertEqual(result, 'prop1=val1&prop3=val3')
89 |
90 | def test_context_chaining(self):
91 | t = Template(
92 | '{% load query_parameters %}'
93 | '{% set_query_parameters prop1=val1 as=result %}'
94 | '{% set_query_parameters prop3=val3 with=result as=modified_result %}'
95 | )
96 | request = HttpRequest()
97 | request.GET = QueryDict('prop2=val2')
98 |
99 | c = RequestContext(request)
100 | result = t.render(c)
101 | self.assertEqual(result, '')
102 | self.assertEqual(c['result'], 'prop1=val1&prop2=val2')
103 | self.assertEqual(c['modified_result'], 'prop1=val1&prop2=val2&prop3=val3')
104 |
105 | def template_generator(self, query_string, set_query_parameters_value):
106 | """
107 | Helper method to simplify generating a template with a mock `query_string` and `set_query_parameters_value`
108 | """
109 | t = Template(
110 | '{% load query_parameters %}'
111 | '{% set_query_parameters ' + set_query_parameters_value + ' %}'
112 | )
113 | request = HttpRequest()
114 | request.GET = QueryDict(query_string)
115 |
116 | c = RequestContext(request)
117 | result = t.render(c)
118 | return result
119 |
120 |
121 |
122 | class DelQueryParametersTestCase(TestCase):
123 | def test_empty_querystring(self):
124 | result = self.template_generator(
125 | query_string='',
126 | del_query_parameters_value='prop1'
127 | )
128 | self.assertEqual(result, '')
129 |
130 | def test_delete_querystring(self):
131 | result = self.template_generator(
132 | query_string='prop1=value1',
133 | del_query_parameters_value='prop1'
134 | )
135 | self.assertEqual(result, '')
136 |
137 | def test_delete_querystring_with_remainders(self):
138 | result = self.template_generator(
139 | query_string='prop1=value1&prop2=value2',
140 | del_query_parameters_value='prop1'
141 | )
142 | self.assertEqual(result, 'prop2=value2')
143 |
144 | def test_delete_multiple_querystrings_with_remainders(self):
145 | result = self.template_generator(
146 | query_string='prop1=value1&prop2=value2&prop3=value3',
147 | del_query_parameters_value='prop1 prop2'
148 | )
149 | self.assertEqual(result, 'prop3=value3')
150 |
151 | def test_delete_nothing(self):
152 | result = self.template_generator(
153 | query_string='prop1=value1',
154 | del_query_parameters_value=''
155 | )
156 | self.assertEqual(result, 'prop1=value1')
157 |
158 | def test_delete_in_context(self):
159 | t = Template(
160 | '{% load query_parameters %}'
161 | '{% del_query_parameters prop2 as=a_result %}'
162 | )
163 | request = HttpRequest()
164 | request.GET = QueryDict('prop1=val1&prop2=val2')
165 |
166 | c = RequestContext(request)
167 | result = t.render(c)
168 | self.assertEqual(result, '')
169 | self.assertEqual(c['a_result'], 'prop1=val1')
170 |
171 | def test_load_from_context(self):
172 | t = Template(
173 | '{% load query_parameters %}'
174 | '{% del_query_parameters prop1 with=existing_querystring %}'
175 | )
176 | request = HttpRequest()
177 | request.GET = QueryDict('prop2=val2')
178 |
179 | c = RequestContext(request, {'existing_querystring':'prop3=val3&prop1=val1'})
180 | result = t.render(c)
181 | self.assertEqual(result, 'prop3=val3')
182 |
183 | def template_generator(self, query_string, del_query_parameters_value):
184 | """
185 | Helper method to simplify generating a template with a mock `query_string` and `del_query_parameters_value`
186 | """
187 | t = Template(
188 | '{% load query_parameters %}'
189 | '{% del_query_parameters ' + del_query_parameters_value + ' %}'
190 | )
191 | request = HttpRequest()
192 | request.GET = QueryDict(query_string)
193 |
194 | c = RequestContext(request)
195 | result = t.render(c)
196 | return result
197 |
198 |
--------------------------------------------------------------------------------
/query_parameters/templatetags/query_parameters.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from django import template
4 | from django.http import QueryDict
5 | from django.conf import settings
6 | import re
7 |
8 | register = template.Library()
9 |
10 | # SETTING PARAMETERS
11 | @register.tag
12 | def set_query_parameters(parser, token):
13 | """
14 | Takes a 1+ list of key=value pairs and generates an updated querystring that includes those pairs. If a key does not already exist
15 | in the querystring, it will be added. If a key exists, it will be updated with the new value
16 |
17 | e.g. querystring = http://localhost/?page=1&limit=20
18 | {% set_query_parameters page=2 order=desc %} => page=2&limit=20&order=desc
19 | """
20 |
21 | params = token.split_contents()[1:]
22 |
23 | # check to see if a special variable output key has been passed (default to `as`);
24 | # if so, we will store the result in the context variable define by the value
25 | VARIABLE_OUTPUT_KEY = getattr(settings, 'QUERY_PARAMETERS_VARIABLE_OUTPUT_KEY', 'as')
26 | params, as_var = pluck_property(params, VARIABLE_OUTPUT_KEY)
27 |
28 | # check to see if a special variable input key has been passed (default to `with`);
29 | # if so, we will pull the query string from the context variable defined by the value
30 | VARIABLE_INPUT_KEY = getattr(settings, 'QUERY_PARAMETERS_VARIABLE_INPUT_KEY', 'with')
31 | params, with_var = pluck_property(params, VARIABLE_INPUT_KEY)
32 |
33 | try:
34 | key_value_dict = dict(key_value_pair.split('=') for key_value_pair in params)
35 | except ValueError:
36 | raise template.TemplateSyntaxError('%r tag requires arguments to be in `key=value` pairs' % token.contents.split()[0])
37 |
38 | return QueryStringSetNode(key_value_dict, as_var, with_var)
39 |
40 | class QueryStringSetNode(template.Node):
41 | def __init__(self, parameter_dict, as_var, with_var):
42 | self.parameter_dict = parameter_dict
43 | self.as_var = as_var
44 | self.with_var = with_var
45 |
46 | def contextual_parameter_dict(self, context):
47 | """
48 | Searches the context for any variables named after the provided parameters; if any found,
49 | replaces the parameter name with the corresponding variable value
50 |
51 | e.g.
52 | first_property = 'test'
53 | {first_property: 'value_1', 'key_2': first_property} => {'test': 'value_1', 'key_2': 'test'}
54 | """
55 |
56 | value_parameter_dict = {}
57 | for key, value in self.parameter_dict.items():
58 | value_parameter_dict[key] = get_value(value, context)
59 | return value_parameter_dict
60 |
61 | def render(self, context):
62 | # if we have been passed a querystring from the context, load it; otherwise, pull from the context
63 | if self.with_var:
64 | query_string = get_value(self.with_var, context)
65 | else:
66 | query_string = get_query_string(context)
67 |
68 | existing_query_dict = QueryDict(query_string.encode('utf8')).copy()
69 |
70 | for key, value in self.contextual_parameter_dict(context).items():
71 | existing_query_dict[key] = value
72 |
73 | result = existing_query_dict.urlencode()
74 |
75 | # if we are storing result as a context property, do so and reutrn a blank string; otherwise, return the value
76 | if self.as_var:
77 | context[self.as_var] = result
78 | return ''
79 | else:
80 | return result
81 |
82 | # DELETING PARAMETERS
83 | @register.tag
84 | def del_query_parameters(parser, token):
85 | """
86 | Takes a 1+ list of keys and generates an updated querystring that removes those keys.
87 |
88 | e.g. querystring = http://localhost/?page=1&limit=20
89 | {% del_query_parameters page order %} => limit=20
90 | """
91 |
92 | params = token.split_contents()[1:]
93 |
94 | # check to see if a special variable output key has been passed (default to `as`);
95 | # if so, we will store the result in the context variable define by the value
96 | VARIABLE_OUTPUT_KEY = getattr(settings, 'QUERY_PARAMETERS_VARIABLE_OUTPUT_KEY', 'as')
97 | params, as_var = pluck_property(params, VARIABLE_OUTPUT_KEY)
98 |
99 | # check to see if a special variable input key has been passed (default to `with`);
100 | # if so, we will pull the query string from the context variable defined by the value
101 | VARIABLE_INPUT_KEY = getattr(settings, 'QUERY_PARAMETERS_VARIABLE_INPUT_KEY', 'with')
102 | params, with_var = pluck_property(params, VARIABLE_INPUT_KEY)
103 |
104 | return QueryStringDeleteNode(params, as_var, with_var)
105 |
106 | class QueryStringDeleteNode(template.Node):
107 | def __init__(self, parameter_delete_list, as_var, with_var):
108 | self.parameter_delete_list = parameter_delete_list
109 | self.as_var = as_var
110 | self.with_var = with_var
111 |
112 | def contextual_parameter_delete_list(self, context):
113 | """
114 | Searches the context for any variables named after the provided parameters; if any found,
115 | replaces the parameter name with the corresponding variable value
116 |
117 | e.g.
118 | first_property = 'test'
119 | [first_property,'second_property'] => ['test','second_property']
120 | """
121 | parameters = map(lambda key: get_value(key, context), self.parameter_delete_list)
122 | return parameters
123 |
124 | def render(self, context):
125 | # if we have been passed a querystring from the context, load it; otherwise, pull from the context
126 | if self.with_var:
127 | query_string = get_value(self.with_var, context)
128 | else:
129 | query_string = get_query_string(context)
130 |
131 | existing_query_dict = QueryDict(query_string).copy()
132 |
133 | for parameter in self.contextual_parameter_delete_list(context):
134 | if existing_query_dict.get(parameter):
135 | del existing_query_dict[parameter]
136 |
137 | result = existing_query_dict.urlencode()
138 |
139 | if self.as_var:
140 | # if we are storing result as a context property, do so and reutrn a blank string
141 | context[self.as_var] = result
142 | return ''
143 | else:
144 | # ... otherwise return the value
145 | return result
146 |
147 |
148 | # HELPER METHDOS
149 | def get_query_string(context):
150 | """
151 | Return the query string from the request context
152 | """
153 | request = context.get('request', None)
154 | if request is None:
155 | return ''
156 | else:
157 | return request.GET.urlencode()
158 |
159 | def get_value(key, context):
160 | """
161 | Return the value of variable `key` from the context if it exists; otherwise, returns the key name
162 | """
163 | try:
164 | value = template.Variable(key)
165 | value = value.resolve(context)
166 | except template.VariableDoesNotExist:
167 | return key
168 | except template.TemplateSyntaxError:
169 | return key
170 | else:
171 | return value
172 |
173 | def pluck_property(params, property_key):
174 | """
175 | Searches a list of params for a `property_key=value` match. If one is found, it stores the value and removes from the list of params
176 | Returns a tuple of (params, value). If no property_key is found, value is None. The returne params will be plucked of the match, if one is found
177 | """
178 | for val in list(params):
179 | match = re.match('%s=(\w+)' % property_key, val)
180 | if match:
181 | params.pop(params.index(val))
182 | return (params, match.group(1))
183 | return (params, None)
184 |
185 |
186 |
--------------------------------------------------------------------------------