query.py

FilteredRawQuerySet

FilteredRawQuerySet inherits Django RawQuerySet class whose instances are returned by Django model object manager .raw() calls.

It supports .filter() / .exclude() / .order_by() / values() / values_list() queryset methods and also SQL-level slicing which is much more efficient than Python slicing of RawQuerySet.

These methods are used by filtering / ordering code in ListSortingView and KoGridView class-based views.

See FilteredRawQuerySet sample in djk-sample project source code for a complete example of AJAX grid with raw query which has LEFT JOIN statement.

Since version 0.4.0 it supports args with Q objects via relation_map argument:

raw_qs = Profile.objects.raw(
    'SELECT club_app_profile.*, club_app_member.is_endorsed, '
    'auth_user.username AS user__username, '
    'CONCAT_WS(\' \', auth_user.last_name, auth_user.first_name) AS fio '
    'FROM club_app_profile '
    'LEFT JOIN club_app_member ON club_app_profile.user_id = club_app_member.profile_id AND '
    'club_app_member.project_id=%s AND club_app_member.role=%s '
    'JOIN auth_user ON auth_user.id = club_app_profile.user_id ',
    params=[self.project.pk, 'watch'],
)
fqs = FilteredRawQuerySet.clone_raw_queryset(
    raw_qs=raw_qs, relation_map={'is_endorsed': 'member'}
)

ListQuerySet

ListQuerySet implements large part of Django queryset functionality for Python lists of Django model instances. Such lists are returned by Django queryset .prefetch_related() method.

This allows to have the same logic of processing queries with both .prefetch_related() applied results and without them. For example, imagine one have two querysets:

from django.db import models
from django.db.models import Prefetch
from django_jinja_knockout.query import ListQuerySet

def process_related():
    qs1 = Project.objects.all()[:10]
    qs2 = Project.objects.all()[:10].prefetch_related(
        Prefetch(
            'projectmember_set',
            to_attr='projectmember_list'
        )
    )
    (obj.process_members() for obj in qs1)
    (obj.process_members() for obj in qs2)

class Project(models.Model):

    # ... skipped ...

    def process_members(self):
        # Detect Prefetch().
        if hasattr(self, 'projectmember_list'):
            qs = ListQuerySet(self.projectmember_list)
        else:
            qs = self.projectmember_set
        # ... Do .filter() / .order_by() / slice operation with qs
        qs_subset = qs.filter(is_approved=False)
        # ... Do some more operations with qs_subset or it's members.
        for obj in qs_subset:
            obj.approve()

class ProjectMember(models.Model):

    project = models.ForeignKey(Project, verbose_name='Project')
    is_approved = models.BooleanField(default=False, verbose_name='Approved member')
    # ... skipped ...

    def approve(self):
        self.is_approved = True
  • Version 0.3.0 implemented .filter() / .exclude() / slicing / .order_by() / .first() / .values() / .values_list() methods. Many but not all of the field lookups are supported. Feel free to submit a pull request if you need more functionality.
  • Version 0.8.0 implemented spanned relationships for .order_by() method.
  • Version 0.8.1 implemented | and + operators for ListQuerySet. Note that the operation does not ensure the uniqueness of the resulting queryset. In case unique rows are required, call .distinct('pk') on the result.
  • Version 2.2.0 implemented basic support of .delete() method (with signals) / .get() method and the most common aggregate functions: Count, Min, Max, Sum.

FutureQuerySet

Aims to provide backward-compatible fallback methods of QuerySet.

Currently has implemented bulk_create_future method, which applies update_conflicts arguments of bulk_create only for Django 4.2 or newer version.