#36751: Django Admin facets broken in 6.0
------------------------------+-----------------------------------------
     Reporter:  Rafael Urben  |                     Type:  Bug
       Status:  new           |                Component:  contrib.admin
      Version:  6.0           |                 Severity:  Normal
     Keywords:                |             Triage Stage:  Unreviewed
    Has patch:  0             |      Needs documentation:  0
  Needs tests:  0             |  Patch needs improvement:  0
Easy pickings:  0             |                    UI/UX:  0
------------------------------+-----------------------------------------
 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'
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36751>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019aa8e38be3-c6799f5d-9dc7-465c-8519-3de1e2ce1cee-000000%40eu-central-1.amazonses.com.

Reply via email to