noripyt / django-cachalot

No effort, no worry, maximum performance.

Home Page:http://django-cachalot.readthedocs.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

NoneType error when saving UserProfileAdmin after Django 4.1 update

PetrDlouhy opened this issue · comments

What happened?

When I press save button on my UserProfile admin page, the following error is thrown:

Traceback (most recent call last):                           
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/core/handlers/exception.py", line 56, in inner
    response = get_response(request)     
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)                                                                                                                                                                                                     
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/sentry_sdk/integrations/django/views.py", line 85, in sentry_wrapped_callback                                                          
    return callback(request, *args, **kwargs)
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/contrib/admin/options.py", line 686, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)    
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/utils/decorators.py", line 134, in _wrapped_view
    response = view_func(request, *args, **kwargs)                                                                                      
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/views/decorators/cache.py", line 62, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/contrib/admin/sites.py", line 242, in inner
    return view(request, *args, **kwargs)                                                                                                                                                                                                                                       
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/contrib/admin/options.py", line 1894, in change_view
    return self.changeform_view(request, object_id, form_url, extra_context)                                                    
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/utils/decorators.py", line 46, in _wrapper
    return bound_method(*args, **kwargs)
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/utils/decorators.py", line 134, in _wrapped_view
    response = view_func(request, *args, **kwargs) 
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/contrib/admin/options.py", line 1751, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)                                                                                                                                                                                                   
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/contrib/admin/options.py", line 1797, in _changeform_view                                                                       
    form_validated = form.is_valid()  
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/forms/forms.py", line 205, in is_valid
    return self.is_bound and not self.errors                    
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/forms/forms.py", line 200, in errors
    self.full_clean()                                                                                                                   
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/forms/forms.py", line 439, in full_clean
    self._post_clean()
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/forms/models.py", line 492, in _post_clean
    self.instance.full_clean(exclude=exclude, validate_unique=False)                                                                                                                                                                                                            
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/db/models/base.py", line 1475, in full_clean
    self.validate_constraints(exclude=exclude)                                                                                          
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/db/models/base.py", line 1423, in validate_constraints
    constraint.validate(model_class, self, exclude=exclude, using=using)
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/db/models/constraints.py", line 365, in validate
    if (self.condition & Exists(queryset.filter(self.condition))).check(
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/django/db/models/query_utils.py", line 141, in check
    return compiler.execute_sql(SINGLE) is not None                                                                                                                                                                                                                             
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/cachalot/monkey_patch.py", line 37, in inner                                                                                           
    return original(compiler, *args, **kwargs)
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/cachalot/monkey_patch.py", line 92, in inner
    table_cache_keys = _get_table_cache_keys(compiler)          
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/cachalot/utils.py", line 276, in _get_table_cache_keys
    for t in _get_tables(db_alias, compiler.query, compiler)]                                                                           
  File "/home/petr/.cache/pypoetry/virtualenvs/blenderkit-server-eqrfB9dY-py3.11/lib/python3.10/site-packages/cachalot/utils.py", line 220, in _get_tables
    tables.add(query.get_meta().db_table)
AttributeError: 'NoneType' object has no attribute 'db_table'

The same error occurs on two more of my admin pages (out of many more).
The exception is not thrown from Django 4.0, but is thrown from Django 4.1.
Also if I remove cachalot from INSTALLED_APPS the problem disapears.

What should've happened instead?

The changes should be saved.

Steps to reproduce

Configuration:

  • Django-cachalot 5.2.0
  • Django 4.1
  • PostgreSQL database
  • System: Linux petr-ws 5.15.0-58-generic #64-Ubuntu SMP Thu Jan 5 11:43:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
  1. Create UserProfile model with some UniqueConstraint, e.g.:
class UserProfile(AbstractUser):
    class Meta:
       constraints = [
            UniqueConstraint(
                Lower("email").desc(),  # type: ignore
                condition=Q(deleted_at=None),
                name="unique_email",
            )
        ]
  1. Go to http://localhost:8000/admin/accounts/userprofile/xxxx/change/
  2. Press save

More details

What I have already find out is that the query with None in query.get_meta() is following:

SELECT 1 AS "_check" WHERE COALESCE((NULL IS NULL AND EXISTS(SELECT 1 AS "a" FROM "accounts_userprofile" U0 WHERE (LOWER(U0."email") = (LOWER()) AND NOT (U0."id" = 897) AND U0."deleted_at" IS NULL) LIMIT 1)), True)