Skip to content

issue while trying to use with_cte on EmptyResultSet #84

@afpekmezci

Description

@afpekmezci

Django has a built-in check when the rhs of the query should return an empty queryset. It raises an EmptyResultSet and returns an empty list for the result. When you combine an EmptyResultSet query with_cte unfortunately, django-cte does not recognize it, and throws an error instead of returning an empty list.

-- queryset -- <CTEQuerySet []>
cte.query 
 SELECT "population_cte"."id", "population_cte"."year", "population_cte"."population" FROM "population_cte" WHERE "population_cte"."population" <= 10000000
--POPULATIONS QUERY : 
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/ahp/Desktop/cte_test/testapp/core/api.py", line 87, in empty_query_issue
    print(
  File "/Users/ahp/Desktop/cte_test/venv/lib/python3.9/site-packages/django/db/models/query.py", line 374, in __repr__
    data = list(self[: REPR_OUTPUT_SIZE + 1])
  File "/Users/ahp/Desktop/cte_test/venv/lib/python3.9/site-packages/django/db/models/query.py", line 398, in __iter__
    self._fetch_all()
  File "/Users/ahp/Desktop/cte_test/venv/lib/python3.9/site-packages/django/db/models/query.py", line 1881, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/Users/ahp/Desktop/cte_test/venv/lib/python3.9/site-packages/django/db/models/query.py", line 99, in __iter__
    model_cls = klass_info["model"]
TypeError: 'NoneType' object is not subscriptable
# simple model
from django.db import models
from django_cte import CTEManager


class Population(models.Model):
    class Meta:
        db_table = "population"

    year = models.PositiveIntegerField()
    population = models.BigIntegerField()

    objects = CTEManager()

    def __str__(self) -> str:
        return f"{self.year} : {self.population}"
# test function

def empty_query_issue():
    queryset = Population.objects.filter(year__in=[])
    # print(
    #     "--queryset.query \n",
    #     queryset.query,
    # )
    print(
        "-- queryset",
        f"-- {queryset}"
    )
    cte = With(
        queryset,
        name="population_cte",
    )
    cte_queryset = cte.queryset()
    cte_queryset = cte_queryset.filter(population__lte=10000000)
    print(
        "cte.query \n",
        cte_queryset.query,
    )

    populations = cte_queryset.with_cte(cte)
    print(
        '--POPULATIONS QUERY : \n',
        populations,
    )

for a quick fix, you can overwrite the __iter__ method of the CTEQuerySet, but this is not a proper solution.

from django_cte import CTEQuerySet
from typing import Iterator
from django.core.exceptions import EmptyResultSet


class FixedCTEQuerySet(CTEQuerySet):

    def __iter__(self) -> Iterator:
        try:
            return super(CTEQuerySet, self).__iter__()
        except Exception:
            try:
                str(self.query)
            except EmptyResultSet:
                return iter([])
            else:
                raise


CTEQuerySet.__iter__ = FixedCTEQuerySet.__iter__

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions