Typorithm · medium

Combine validated filters, search and allowed ordering

pythondjangodrfindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Combine validated filters, search and allowed ordering

0%

Concept and contract

Apply query features to the already scoped collection.

A useful collection API can combine country filters, a numeric minimum, text search and ordering. Those features should refine the permitted collection rather than replace its ownership policy. Invalid filter values also need a clear response instead of silently changing the query.

The fixture uses django-filter with explicit country choices and a slots minimum. DRF SearchFilter and OrderingFilter add title search and a reviewed ordering field list. The base queryset remains restricted to owned alice rows before any client-selected refinement.

Mechanism and worked example

DjangoFilterBackend applies the declared FilterSet and reports invalid country choices.

DjangoFilterBackend applies the declared FilterSet and reports invalid country choices. SearchFilter uses the configured title field, while OrderingFilter allows only id, title and slots. The serializer emits a small public representation after those query operations.

Tests combine country and minimum slots, exercise search, and reject an unsupported country. The local query uses real model rows and the helper reports exact returned IDs. A fixed title can appear in several employers in production, so this example does not pretend that search terms are vacancy identities.

Complete worked example

python

from drf_lab import setup
setup()

import django_filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, generics, serializers
from rest_framework.test import APIRequestFactory
from drf_lab import reset_fixture
from drf_lab.models import Role

class RoleFilter(django_filters.FilterSet):
    country = django_filters.ChoiceFilter(choices=[(c, c) for c in ["US", "IL", "UA", "CA"]])
    slots_min = django_filters.NumberFilter(field_name="slots", lookup_expr="gte")
    class Meta:
        model = Role
        fields = ["country", "slots_min"]
class PublicRole(serializers.ModelSerializer):
    class Meta:
        model = Role
        fields = ["id", "title", "country"]
class RoleList(generics.ListAPIView):
    serializer_class = PublicRole
    queryset = Role.objects.filter(owner__username="alice").order_by("id")
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_class = RoleFilter
    search_fields = ["title"]
    ordering_fields = ["id", "title", "slots"]

def inspect_example(query):
    reset_fixture()
    response = RoleList.as_view()(APIRequestFactory().get("/roles/", query))
    body = {key: [e.code for e in values] for key, values in response.data.items()} if response.status_code >= 400 else [row["id"] for row in response.data]
    return {"status": response.status_code, "body": body}
Reasoning
DjangoFilterBackend applies the declared FilterSet and reports invalid country choices. SearchFilter uses the configured title field, while OrderingFilter allows only id, title and slots. The serializer emits a small public representation after those query operations. Tests combine country and minimum slots, exercise search, and reject an unsupported country. The local query uses real model rows and the helper reports exact returned IDs. A fixed title can appear in several employers in production, so this example does not pretend that search terms are vacancy identities.

Focused decision: context is in the complete example

python

filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = RoleFilter
search_fields = ["title"]
ordering_fields = ["id", "title", "slots"]
Focused decision
Reconstruct this focused decision after the warm-up. The complete imports, model or application, and test client remain in the worked example. Typing records practice; only the downloadable computer lab executes Python.

Counterexamples and common mistakes

The counterexample removes DjangoFilterBackend from the backend list.

The counterexample removes DjangoFilterBackend from the backend list. Search and ordering can still work, making the interface appear partly functional, but country and numeric filters no longer constrain the result. Tests must cover combinations instead of verifying each control only by its visible label.

Avoid exposing every serializer or model field as an ordering option. Private values can affect observable order even when not displayed. Also distinguish an unsupported ordering request, which this backend can ignore in favor of defaults, from a filter validation failure that produces a client error.

Controlled mistake: observe the actual wrong behavior

python

from drf_lab import setup
setup()

import django_filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, generics, serializers
from rest_framework.test import APIRequestFactory
from drf_lab import reset_fixture
from drf_lab.models import Role

class RoleFilter(django_filters.FilterSet):
    country = django_filters.ChoiceFilter(choices=[(c, c) for c in ["US", "IL", "UA", "CA"]])
    slots_min = django_filters.NumberFilter(field_name="slots", lookup_expr="gte")
    class Meta:
        model = Role
        fields = ["country", "slots_min"]
class PublicRole(serializers.ModelSerializer):
    class Meta:
        model = Role
        fields = ["id", "title", "country"]
class RoleList(generics.ListAPIView):
    serializer_class = PublicRole
    queryset = Role.objects.filter(owner__username="alice").order_by("id")
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    filterset_class = RoleFilter
    search_fields = ["title"]
    ordering_fields = ["id", "title", "slots"]

def inspect_example(query):
    reset_fixture()
    response = RoleList.as_view()(APIRequestFactory().get("/roles/", query))
    body = {key: [e.code for e in values] for key, values in response.data.items()} if response.status_code >= 400 else [row["id"] for row in response.data]
    return {"status": response.status_code, "body": body}
Reasoning
The counterexample removes DjangoFilterBackend from the backend list. Search and ordering can still work, making the interface appear partly functional, but country and numeric filters no longer constrain the result. Tests must cover combinations instead of verifying each control only by its visible label. Avoid exposing every serializer or model field as an ordering option. Private values can affect observable order even when not displayed. Also distinguish an unsupported ordering request, which this backend can ignore in favor of defaults, from a filter validation failure that produces a client error.

Independent practice

Change title search from contains matching to prefix matching by using the ^ search-field prefix.

Change title search from contains matching to prefix matching by using the ^ search-field prefix. Preserve country validation, the numeric filter and allowed ordering. A middle fragment should stop matching while a true prefix remains useful.

Test search=eb, search=We and the Ukrainian collection ordered by descending slots. The first should become empty, the second retain Web and the combined country ordering should remain correct. Do not implement the task by changing fixture titles or bypassing the search backend.

Step 1: Use prefix title search while preserving validated country filters and allowed ordering. Step 2: Retain the working imports and application structure. Change the stated policy, run the supplied practice cases, then explain the newly accepted or rejected boundary.

Practice starter: solve before reading the solution

python

from drf_lab import setup
setup()

import django_filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, generics, serializers
from rest_framework.test import APIRequestFactory
from drf_lab import reset_fixture
from drf_lab.models import Role

class RoleFilter(django_filters.FilterSet):
    country = django_filters.ChoiceFilter(choices=[(c, c) for c in ["US", "IL", "UA", "CA"]])
    slots_min = django_filters.NumberFilter(field_name="slots", lookup_expr="gte")
    class Meta:
        model = Role
        fields = ["country", "slots_min"]
class PublicRole(serializers.ModelSerializer):
    class Meta:
        model = Role
        fields = ["id", "title", "country"]
class RoleList(generics.ListAPIView):
    serializer_class = PublicRole
    queryset = Role.objects.filter(owner__username="alice").order_by("id")
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_class = RoleFilter
    search_fields = ["title"]
    ordering_fields = ["id", "title", "slots"]

def inspect_example(query):
    reset_fixture()
    response = RoleList.as_view()(APIRequestFactory().get("/roles/", query))
    body = {key: [e.code for e in values] for key, values in response.data.items()} if response.status_code >= 400 else [row["id"] for row in response.data]
    return {"status": response.status_code, "body": body}

# Independent task: Use prefix title search while preserving validated country filters and allowed ordering.
Reasoning
Use prefix title search while preserving validated country filters and allowed ordering.
  • Hint 1: Find the one field or branch that owns this policy.
  • Hint 2: Compare the first changed acceptance case with the retained baseline.

Solution, tests and trade-offs

The solution changes search_fields to ^title.

The solution changes search_fields to ^title. The same owned queryset passes through the remaining backends. The result demonstrates a precise text policy change while preserving filtering and ordering behavior.

For large datasets, measure actual query plans and index usefulness before promising speed. Case-insensitive contains and prefix lookups have backend-specific costs. A small in-memory fixture establishes semantics, not production latency or completeness of a market collection.

Practice solution and acceptance checks

python

from drf_lab import setup
setup()

import django_filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, generics, serializers
from rest_framework.test import APIRequestFactory
from drf_lab import reset_fixture
from drf_lab.models import Role

class RoleFilter(django_filters.FilterSet):
    country = django_filters.ChoiceFilter(choices=[(c, c) for c in ["US", "IL", "UA", "CA"]])
    slots_min = django_filters.NumberFilter(field_name="slots", lookup_expr="gte")
    class Meta:
        model = Role
        fields = ["country", "slots_min"]
class PublicRole(serializers.ModelSerializer):
    class Meta:
        model = Role
        fields = ["id", "title", "country"]
class RoleList(generics.ListAPIView):
    serializer_class = PublicRole
    queryset = Role.objects.filter(owner__username="alice").order_by("id")
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_class = RoleFilter
    search_fields = ["^title"]
    ordering_fields = ["id", "title", "slots"]

def inspect_example(query):
    reset_fixture()
    response = RoleList.as_view()(APIRequestFactory().get("/roles/", query))
    body = {key: [e.code for e in values] for key, values in response.data.items()} if response.status_code >= 400 else [row["id"] for row in response.data]
    return {"status": response.status_code, "body": body}
Reasoning
The solution changes search_fields to ^title. The same owned queryset passes through the remaining backends. The result demonstrates a precise text policy change while preserving filtering and ordering behavior. For large datasets, measure actual query plans and index usefulness before promising speed. Case-insensitive contains and prefix lookups have backend-specific costs. A small in-memory fixture establishes semantics, not production latency or completeness of a market collection.

Interview reasoning and next step

Explain the backend pipeline and name the assertion that catches a country filter silently disappearing.

Explain the backend pipeline and name the assertion that catches a country filter silently disappearing. Then distinguish allowed ordering fields from public serialized fields and from verified user identity. Each is a separate design decision.

Next, paginate the selected collection. A count should describe the same filtered scope as its rows, and client-selected page size must remain bounded even when the request asks for more.

Optional stretch — after the core task works: Add one independently reasoned boundary case and explain its expected value before running it.

Computer practice: https://applydjinn.com/typorithm/drf-lab-20261105.zip. Install pinned dependencies in an isolated Python environment. Run verify.py, then change the working exercise baseline and run --practice drf-filtering. The real DRF clients, serializers, schemas and SQLite transactions use owned fixtures only. No production database, feed, account or paid service is used. This lab executes Python; the PC typing trainer and native reading progress do not execute it.

Apply query features to the already scoped collection.