#36751 new Bug

Django Admin facets broken in 6.0

Reported by: Rafael Urben Owned by:
Component: contrib.admin Version: 6.0
Severity: Normal Keywords:
Cc: Rafael Urben Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

In Django 6.0, activating facets leads to an internal server error when the admin queryset contains certain annotations.

As an example, the following annotations lead to an error:

def get_queryset(self, request):
    return (
        super()
        .get_queryset(request)
        .annotate(
            annotation_has_usable_password=Case(
                When(
                    Q(password__isnull=False) & ~Q(password__startswith="!"), then=Value(True)
                ),
                default=Value(False),
                output_field=BooleanField(),
            ),
        )
    )

The error observed seems to depend on the database backend:

  • With sqlite3, I get 'NoneType' object has no attribute 'as_sql' (triggered in template rendering at site-packages\django\contrib\admin\templates\admin\change_list.html, error at line 72)
  • With mysql, I get When() supports a Q object, a boolean expression, or lookups as a condition. (triggered in template rendering at site-packages\django\contrib\admin\templates\admin\change_list.html, error at line 72)

I have created a small gist with a minimal reproducer admin.py that triggers the error:
https://gist.github.com/rafaelurben/670658ffe1a9cc0cfee45380e8f148a0
The example works without an issue in Django 5.2.8 but fails on Django 6.0rc1 (tested in a new project with a new venv with sqlite and in an existing project with mysql).

I'm not sure if I have missed something in the Django 6.0 release notes, but this looks like a bug.

Traceback (sqlite):

Environment:


Request Method: GET
Request URL: http://localhost:8000/admin/auth/user/?_facets=True

Django Version: 6.0rc1
Python Version: 3.13.5
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'testapp']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Template error:
In template C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\contrib\admin\templates\admin\change_list.html, error at line 72
   'NoneType' object has no attribute 'as_sql'
   62 :             <h2 id="changelist-filter-header">{% translate 'Filter' %}</h2>
   63 :             {% if cl.is_facets_optional or cl.has_active_filters %}<div id="changelist-filter-extra-actions">
   64 :               {% if cl.is_facets_optional %}<h3>
   65 :                 {% if cl.add_facets %}<a href="{{ cl.remove_facet_link }}" class="hidelink">{% translate "Hide counts" %}</a>
   66 :                 {% else %}<a href="{{ cl.add_facet_link }}" class="viewlink">{% translate "Show counts" %}</a>{% endif %}
   67 :               </h3>{% endif %}
   68 :               {% if cl.has_active_filters %}<h3>
   69 :                 <a href="{{ cl.clear_all_filters_qs }}">&#10006; {% translate "Clear all filters" %}</a>
   70 :               </h3>{% endif %}
   71 :             </div>{% endif %}
   72 :             {% for spec in cl.filter_specs %} {% admin_list_filter cl spec %} {% endfor %}
   73 :           </search>
   74 :           {% endif %}
   75 :         {% endblock %}
   76 :         <div>
   77 :           {% block search %}{% search_form cl %}{% endblock %}
   78 :           {% block date_hierarchy %}{% if cl.date_hierarchy %}{% date_hierarchy cl %}{% endif %}{% endblock %}
   79 : 
   80 :           <form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>{% csrf_token %}
   81 :           {% if cl.formset %}
   82 :             <div>{{ cl.formset.management_form }}</div>


Traceback (most recent call last):
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\core\handlers\base.py", line 221, in _get_response
    response = response.render()
               ^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\response.py", line 114, in render
    self.content = self.rendered_content
                   ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\response.py", line 92, in rendered_content
    return template.render(context, self._request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\backends\django.py", line 107, in render
    return self.template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 173, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 165, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1089, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1050, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\loader_tags.py", line 160, in render
    return compiled_parent._render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 165, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1089, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1050, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\loader_tags.py", line 160, in render
    return compiled_parent._render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 165, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1089, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1050, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\loader_tags.py", line 66, in render
    result = block.nodelist.render(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1089, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1050, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\loader_tags.py", line 66, in render
    result = block.nodelist.render(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1089, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1050, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\defaulttags.py", line 333, in render
    return nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1089, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1050, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\defaulttags.py", line 249, in render
    nodelist.append(node.render_annotated(context))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\base.py", line 1050, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\template\library.py", line 322, in render
    output = self.func(*resolved_args, **resolved_kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\contrib\admin\templatetags\admin_list.py", line 517, in admin_list_filter
    "choices": list(spec.choices(cl)),
               ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\contrib\admin\filters.py", line 543, in choices
    facet_counts = self.get_facet_queryset(changelist) if add_facets else None
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\contrib\admin\filters.py", line 87, in get_facet_queryset
    return filtered_qs.aggregate(
           
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\query.py", line 594, in aggregate
    return self.query.chain().get_aggregation(self.db, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\query.py", line 633, in get_aggregation
    result = compiler.execute_sql(SINGLE)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 1611, in execute_sql
    sql, params = self.as_sql()
                  ^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 767, in as_sql
    extra_select, order_by, group_by = self.pre_sql_setup(
                                       
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 86, in pre_sql_setup
    self.setup_query(with_col_aliases=with_col_aliases)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 75, in setup_query
    self.select, self.klass_info, self.annotation_col_map = self.get_select(
                                                            
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 317, in get_select
    sql, params = self.compile(col)
                  ^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 576, in compile
    sql, params = vendor_impl(self, self.connection)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\expressions.py", line 29, in as_sqlite
    sql, params = self.as_sql(compiler, connection, **extra_context)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\aggregates.py", line 193, in as_sql
    filter_sql, filter_params = compiler.compile(self.filter)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 576, in compile
    sql, params = vendor_impl(self, self.connection)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\expressions.py", line 29, in as_sqlite
    sql, params = self.as_sql(compiler, connection, **extra_context)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\aggregates.py", line 47, in as_sql
    return super().as_sql(compiler, connection, **extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\expressions.py", line 1107, in as_sql
    arg_sql, arg_params = compiler.compile(arg)
                          ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\rafae\Coding\Django\reproducer_dj60_facets\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 578, in compile
    sql, params = node.as_sql(self, self.connection)
                  ^^^^^^^^^^^

Exception Type: AttributeError at /admin/auth/user/
Exception Value: 'NoneType' object has no attribute 'as_sql'

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top