Skip to content

Query Parameters#

2024 iThome Ironman

This is the 11th post in the Django Ninja tutorial series.

In the previous post, we discussed how to handle path parameters in the request URL.

This article will introduce query parameters, which are an important part of RESTful APIs used to pass filtering conditions and other additional information.

Handling query parameters in Django Ninja is very simple and intuitive, and we can achieve it in multiple ways.

For all code changes in this article, please refer to this PR.

GitHub Example Project#

👉 Django-Ninja-Tutorial


1. What Are Query Parameters?#

Query parameters are optional parameters in a URL, usually located after the path in the form of ?key=value, used to pass additional information.

For example, when we need to filter posts by a specific author, the URL path might look like this:

/posts/?author=john

The URL passes a query parameter author=john, indicating that we want to filter out posts written by John.

2. Changes in the Example Project#

To introduce query parameters more realistically, we need to modify the original "Get All Posts" API to add a simple "filtering" feature.

More complex filtering features will be introduced in Article 23, "Filtering."

After the modification, when a request carries query parameters, the API can use these parameters to limit the query results, as follows:

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = None):
    posts = Post.objects.all()
    if title:
        posts = posts.filter(title__icontains=title)  # Implement filtering logic
    return posts

Here, we filter by "post title".

Note: The project API cannot be used yet; first, there is no data in the database, and second, we have not written the corresponding Schema yet. At this stage, it only serves as a reference for reading and understanding. But don't worry, we will make it work very soon ☺️

Alright, now that the code has been modified, let's explain it.


3. Using Query Parameters in Django Ninja#

In Django Ninja, the simplest way to handle query parameters is to directly make them optional parameters of the view function—using a default parameter value of None:

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = None):

In this example, the title parameter is defined as an optional string (None | str = None).

  • If the URL contains the title query parameter, Django Ninja will automatically pass its value as an argument to the get_posts function.
  • If the URL does not contain this Query Parameter, title will be None inside the function because that is its Default Value.

Regarding this example, we also need to keep in mind the following points:

  1. Query parameters do not need to be written in the path parameter path of the router decorator.
  2. Query parameters usually have default values, whether it's a specific value or the aforementioned None. If a default value is missing, Django Ninja will return a 422 response when the query parameter is absent.
  3. When the default value is None, pay attention to how the type hints are written: None | str = None (equivalent to Optional[str] = None).
  4. Just like path parameters, query parameters undergo type conversion based on the function's type hints. If no type is hinted, the default type for both is str—because URLs are essentially strings.

The above approach is simple and direct, suitable for most situations.

However, when we need more complex validation or restrictions on query parameters, we need to use an advanced technique—Query.


4. Using the Query Object#

When we need more detailed control, such as restricting the length or range of query parameters, or adding extra information to the API documentation, we can use Query to configure and handle query parameters.

I must admit that I rarely used Query in my previous development, but understanding its top 20% most important features will definitely be very helpful.

Introduction to Query#

Through the Query object, we can define and validate query parameters with finer precision.

In fact, if you look at the source code of Django Ninja, you will find that it is actually a function that returns a class object of the same name.

For convenience of explanation, we will collectively refer to it as the Query object. After all, in Python, everything is an object.

Refer to this modified example:

from ninja import Query, Router
...

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = Query(None)):
    ...

It is almost equivalent to the original code below (there are still minor differences, but they can be ignored for now):

def get_posts(request: HttpRequest, title: None | str = None):

You might think it is strange: why would I switch to a more complex syntax if there are no extra benefits?

This is, of course, because the more complex syntax allows you to do more things.

Restricting the Length of Query Strings#

For example, if we want to restrict the title query string so that it cannot be too long or too short.

Suppose we require the length to be between 2 and 10 characters.

In this case, you can write:

def get_posts(
    request: HttpRequest,
    title: None | str = Query(None, min_length=2, max_length=10),
):

In this example, we used Query to define the title query parameter and provided min_length and max_length settings to initialize the Query object.

This ensures that the length of the title query parameter remains between 2 and 10 characters.

If the title input by the user does not meet this length requirement, as described in the previous article, Django Ninja will automatically return a response with status code 422, without us having to manually handle these validation logics and related responses.

// 422 Unprocessable Entity
{
    "detail": [
        {
            "type": "string_too_short",  // Query parameter is too short
            "loc": [
                "title",
                "title"
            ],
            "msg": "String should have at least 2 characters",
            "ctx": {
                "min_length": 2
            }
        }
    ]
}

Other Common Parameters of Query#

In addition to min_length and max_length, Query provides many practical parameters for you to restrict query conditions and supplement API documentation. The common ones are:

  • gt, ge: The value of the query parameter must be greater than or greater than or equal to a certain number.
  • lt, le: The value of the query parameter must be less than or less than or equal to a certain number.
  • example, examples: Providing example values of the query parameter for API documentation, making it easier for users to understand parameter usage.

We won't demonstrate this part.


Summary and Next Steps#

Query parameters are a common and important component of RESTful APIs. In Django Ninja, we can handle query parameters in a simple manner, or we can use Query for more advanced validation and control.

Now that we understand how Django Ninja handles parameters related to URLs, the next part is the main event.

Next, we will explore how to handle HTTP request bodies in Django Ninja, introducing how to use Schema for data validation and deserialization, allowing us to flexibly process complex request information.