Skip to content

The "Power of Select"

The select operator

The select operator is very powerful and is expecially useful for REST APIs.

Suppose you have the following models:

class Category(models.Model):
    name = models.CharField(max_length=100)

class Company(models.Model):
    name = models.CharField(max_length=100)
    vat_number = models.CharField(max_length=15)

class Product(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    manufacturer = models.ForeignKey(Company, on_delete=models.CASCADE)

and the following filter class:

from dj_rql.filter_cls import RQLFilterClass
from dj_rql.qs import SelectRelated

class ProductFilters(RQLFilterClass):

    MODEL = Product
    SELECT = True
    FILTERS = (
        'name',
        {
            'namespace': 'category',
            'filters': ('name',),
            'qs': SelectRelated('category'),
        },
        {
            'namespace': 'manufacturer',
            'filters': ('name', 'vat_number'),
            'hidden': True,
            'qs': SelectRelated('manufacturer'),
        }
    )

Issuing the following query:

GET /products?ilike(name,*rql*)

Behind the scenes django-rql applies a select_releted optimization to the queryset to retrive the category of each product doing a SQL JOIN.

Since the manufacturer has been declared hidden django-rql doesn't retrive the related manufacturer unless you write:

GET /products?ilike(name,*rql*)&select(manufacturer)

If you issue such query, django-rql apply the qs database optimization so it adds a JOIN with the Company model to optimize database access.

The select operator can also be used to exclude fields so if you want to retrieve products without retrieving the associated category you can write:

GET /products?ilike(name,*rql*)&select(-category)

So the category will be not fetched.

Django Rest Framework support

If you are writing a REST API with Django Rest Framework, django-rql offers an utility mixin dj_rql.drf.serializers.RQLMixin for your model serializers to automatically adjust the serialization of related models depending on select.

from rest_framework import serializers

from dj_rql.drf.serializers import RQLMixin

from ..models import Category, Company, Product


class CategorySerializer(RQLMixin, serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ('id', 'name')


class CompanySerializer(RQLMixin, serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = ('id', 'name')


class ProductSerializer(RQLMixin, serializers.ModelSerializer):
    category = CategorySerializer()
    company = CompanySerializer()

    class Meta:
        model = Product
        fields = ('id', 'name', 'category', 'company')

Note

A complete working example of how the select operator works can be found here.