Bjarte
Bjarte

Reputation: 2287

Django url template with query parameters

I'm trying to pass query parameters via my view into a link, but it escapes me on how to actually achieve this in a good way.

My template is as following:

<a class="link-button" href="{% url 'videos:index' %}?tag={{ tag }}&page={{ next }}">Next</a>

This returns what I want:

http://127.0.0.1:8000/videos/?tag=1&page=2

While this works, it's quite fragile, does not handle None values and there must be a better way of doing this.

I tried to pass this via the urltemplate tag but it did not seem to be what I was looking for since it requires url config changes for path:

{% url 'videos:index' page=next tag=tag %}

Is there an actual way of doing this or a template tag I can use to get the parameters? I tried searching for this but it gave me a lot of old results and more path urls, like: /videos/page-1/tag-1/ which I'm not looking for.

I was hoping to do something like:

<a href="{% url 'videos:index'}?{% params page=next tag=tag %}">Next</a>

Upvotes: 14

Views: 18555

Answers (4)

Mathieu Dhondt
Mathieu Dhondt

Reputation: 8914

Django 5.1 introduced the querystring template tag (docs):

Basic usage

{% querystring %}

Outputs the current query string verbatim. So if the query string is ?color=green, the output would be ?color=green.

{% querystring size="M" %}

Outputs the current query string with the addition of the size parameter. Following the previous example, the output would be ?color=green&size=M.

Custom QueryDict

{% querystring my_query_dict %}

You can provide a custom QueryDict to be used instead of request.GET. So if my_query_dict is <QueryDict: {'color': ['blue']}>, this outputs ?color=blue.

Setting items

{% querystring color="red" size="S" %}

Adds or modifies parameters in the query string. Each keyword argument will be added to the query string, replacing any existing value for that key. For instance, if the current query string is ?color=green, the output will be ?color=red&size=S.

Removing items

{% querystring color=None %}

Passing None as the value removes the parameter from the query string. For example, if the current query string is ?color=green&size=M, the output will be ?size=M.

Handling lists

{% querystring color=my_list %}

If my_list is ["red", "blue"], the output will be ?color=red&color=blue, preserving the list structure in the query string.

Dynamic usage

A common example of using this tag is to preserve the current query string when displaying a page of results, while adding a link to the next and previous pages of results. For example, if the paginator is currently on page 3, and the current query string is ?color=blue&size=M&page=3, the following code would output ?color=blue&size=M&page=4:

{% querystring page=page.next_page_number %}

You can also store the value in a variable. For example, if you need multiple links to the same page, define it as:

{% querystring page=page.next_page_number as next_page %}

Upvotes: 0

Flimm
Flimm

Reputation: 151027

Template tag urlencode

Use the template tag urlencode:

<a href="example?tag={{ tag|urlencode }}&page={{ next|urlencode }}">example</a>

Small note:

Note that there is no need to pass the param names themselves through urlencode, since in this case they are literals. If the param names were not literals, you would need to, like this:

<a href="example?{{ name1|urlencode }}={{ value1|urlencode }}&{{ name2|urlencode }}={{ value2|urlencode }}">example</a>

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477160

There is no builtin support, but you can add one yourself. You can for example define the following template tag. We can for example construct files in boldface:

app/
    templatetags/
        __init__.py
        urlparams.py

Where in urlparams.py, we define:

from django import template
from urllib.parse import urlencode

register = template.Library()

@register.simple_tag
def urlparams(*_, **kwargs):
    safe_args = {k: v for k, v in kwargs.items() if v is not None}
    if safe_args:
        return '?{}'.format(urlencode(safe_args))
    return ''

In the template, we can then load the template tag and then use it like with:

{% load urlparams %}

<a href="{% url 'videos:index'}{% urlparams page='1' tag='sometag' %}">Next</a>

Note that strictly speaking, the URL parameters can contain the same key multiple times. This is here not possible. So we can not generate all possible URL parameters, but this is usually quite rare, and in my opinion not a good idea in the first place.

Upvotes: 16

vorujack
vorujack

Reputation: 1950

you can use default template filter and update your example

<a class="link-button" href="{% url 'videos:index' %}?tag={{ tag|default:'' }}&page={{ next|defaul:'' }}">Next</a>

output for empty tag and page is:

http://127.0.0.1:8000/videos/?tag=&page=

but if you want to dont print None Tag in url you must your own template tag or filter. simply you can write this template filter

@register.filter
def print_query_param(value, key)
   if value and key:
       return "%s=%s&" % (key, value)

and you can use it as below

<a class="link-button" href="{% url 'videos:index' %}?{{ tag|print_query_param:'tag' }}{{ next|print_query_param:'page' }}">Next</a>

Upvotes: 3

Related Questions