This tiny package adds the {% querystring %}
tag: A powerful, well tested template tag for modifying and rendering safe, suitably-encoded querystring values.
It's the clean and simple way to create pagination links, filters and other state-preserving links, without cluttering up your view code!
-
Install the package from pypi:
With Poetry
$ poetry add django-querystring-tag
With pip
$ pip install django-querystring-tag
-
Add
"querystring_tag"
to theINSTALLED_APPS
list in your Django project settings.
To use the {% querystring %}
tag freely, without having to add {% load querystring_tag %}
to all of your templates, you can add "querystring_tag.templatetags.querystring_tag"
to the ['OPTIONS']['builtins']
list for your chosen template backend. Here's an example.
First, load the tag into the template you want to use it in:
{% load querystring_tag %}
You can then use the tag like this:
{% querystring param_one='value' param_two+='add-to-existing' param_three-="remove-from-existing" %}
- The tag uses
request.GET
as the data source by default. Check out thesource_data
option if you have other ideas. - The examples below are deliberately simple: You can make as many modifications in the same tag as you need. GO CRAZY!
- You may be wondering "I want to use this in an include template, where the parameter name is dynamic. Will that work?". Yes it will! I know it's unusual, but you can use tempalate variables for parameter names too.
- You don't want to preserve Google tracking parameters in links, do you? I thought not. Any parameters starting with
utm_
are removed by default. Addremove_utm=False
if you would rather keep them. - You're probably not interested in preserving blank parameters in links either, are you? See? I read your mind! Blank values are removed by default too. Add
remove_blank=False
if you would rather keep them. - Want to variabalize the return value instead of rendering it? Go ahead and try the
as
option - It works just as you would expect.
The most common requirement is to completely replace the value for a specific parameter. This is done using a regular keyword argument, with an =
operator between the parameter name and value. For example, if your querystring looked like this:
?q=test&baz=1
And you wanted to add a foo
variable with the value bar
, your querystring tag might look like this:
{% querystring foo="bar" %}
Which would result in the following output:
?q=test&baz=1&foo=bar
When working with multi-value parameters, you may find yourself having to remove a specific value, without affecting any of the others.
In these situations, you can use the -=
operator instead of the usual =
. For example, if the current querystring looked something like this:
?q=test&bar=1&bar=2&bar=3
And you wanted to remove &bar=2
, your querystring tag might look like this:
{% querystring bar-=2 %}
Which would result in the following output:
?q=test&bar=1&bar=3
If the specified value isn't present, the instruction will simply be ignored.
When working with multi-value parameters, you may find yourself having to add a specific value for a parameter, without affecting any of the others.
In these situations, you can use the +=
operator instead of the usual =
. For example, if the current querystring looked something like this:
?q=test&bar=1&bar=2&bar=3
And you wanted to add &bar=4
, your querystring tag might look like this:
{% querystring bar+=4 %}
Which would result in the following output:
?q=test&bar=1&bar=2&bar=3&bar=4
If the specified value is already present, the instruction will simply be ignored.
Use only
at the start of your {% querystring %}
tag when you want the querystring to include values for specific parameters only.
For example, say the current querystring looked like this:
?q=keywords&group=articles&category=2&published_after=2022-01-01
And you only wanted to include the q
and group
params in a link. You could do:
{% querystring only 'q' 'group' %}
Which would result in the following output:
?q=keywords&group=articles
You can combine only
with any number of modifications too. Just be sure to keep the only
keyword and related parameter names as the left-most parameters, like so:
{% querystring only 'q' group="group_value" clear_session="true" %}
Use discard
at the start of your {% querystring %}
tag when you want to exclude specific parameters from the querystring.
For example, say the current querystring looked like this:
?q=keywords&group=articles&category=2&published_after=2022-01-01
And you wanted to preserve everything except for group
published_after
. You could do:
{% querystring discard 'group' 'published_after' %}
Which would result in the following output:
?q=keywords&group=articles
You can combine discard
with any number of modifications too. Just be sure to keep the discard
keyword and related parameter names as the left-most parameters, like so:
{% querystring discard 'published_after' group="group_value" clear_session="true" %}
Unlike a lot of custom template tags, {% querystring %}
supports the use of template variables in keys as well as values. For example, if the tag was being used to generate pagination links, and page_param_name
and page_num
were variables available in the template, you could use them both like so:
{% querystring page_param_name=page_num %}
Values can be strings, booleans, integers, dates, datetimes, Django model instances, or iterables of either of these values.
When encountering a Django model instance, {% querystring %}
will automatically take the pk
value from it, and use that to modify the querystring. To use a different field value, you can use the tag's model_value_field
option (see further down for details). Alternatively, you can add a querystring_value_field
attribute to your model class. For example:
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True)
# use 'slug' values in querystrings
querystring_value_field = "slug"
As mentioned above, you can provide an iterable as a value to specify multiple values for a parameter at once. That could be a native Python type, such as a list
, tuple
or set
, but could also be anything that implements the __iter__
method to support iteration, for example, a QuerySet
.
For example, if the context contained a variable tag_list
, which was list of strings (['tag1', 'tag2', 'tag3']
), you can include all
of those values by referencing the list value. For example:
{% querystring tags=tag_list %}
The output of the above would be:
"?tags=tag1&tags=tag2&tags=tag3"
Supported value types: QueryDict
, dict
, str
Default value: request.GET
The tag defaults to using request.GET
as the data source for the querystring, but the source_data
keyword argument can be used to specify use an alternative QueryDict
, dict
or string value.
For example, say you were using a Django form to validate query data, and only want valid data to be included. You could use the Form's cleaned_data
to generate a querystring instead:
{% querystring source_data=form.cleaned_data page=2 %}
Supported value types: bool
Default value: True
Any parameter values with a value of None
or ""
(an empty string) are removed from the querystring default.
To retain blank values, include remove_blank=False
in your {% querystring %}
tag.
Supported value types: bool
Default value: True
Parameter names starting with "utm_"
(the format used for Google Analytics tracking parameters) are exluded from the generated querystrings by default, as it's unlikley that you'll want these to be repeated in links to other pages.
To retain these parameters instead, include remove_utm=False
in your {% querystring %}
tag.
Supported value types: str
Default value: "pk"
By default, when encountering a Django model instance as a value, {% querystring %}
will take the pk
value from the instance to use in the querystring. If you'd like to use a different field value, you can use the model_value_field
option to specify an alternative field.
For example, if the model had a slug
field that you were using as the public-facing identifier, you could specify that slug
values be used in the querystring, like so:
{% querystring tags=tag_queryset model_field_value='slug' %}
If you have a recent version of Python 3 installed, you can use a simple virtualenv to run tests locally. After cloning the repository, navigate to the project's root directory on your machine, then run the following:
$ virtualenv venv
$ source venv/bin/activate
$ pip install -e '.[test]' -U
$ pytest
$ deactivate