Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when deleting a role from the Admin site #60

Open
stgraham2000 opened this issue Aug 5, 2020 · 7 comments
Open

Error when deleting a role from the Admin site #60

stgraham2000 opened this issue Aug 5, 2020 · 7 comments

Comments

@stgraham2000
Copy link

stgraham2000 commented Aug 5, 2020

If you try to delete a role from the admin interface on Django 3.0.9 you get:

Request Method:  POST
Request URL: http://localhost:8080/admin/django_prbac/role/1/change/
Django Version: 3.0.9
Exception Type: TypeError
Exception Value: unhashable type: 'UserRole'
Exception Location: /usr/local/lib/python3.7/site-packages/django/contrib/admin/utils.py in collect, line 179
Python Executable: /usr/local/bin/python
Python Version: 3.7.7
...
@millerdev
Copy link
Contributor

I think I need more of a stack trace to troubleshoot.

render() got an unexpected keyword argument 'renderer'

Looks like the same error fixed by #61 —is this a duplicate?

@stgraham2000
Copy link
Author

I wish it was a duplicate of #61 but it’s not. I’ll dig up better trace for you. My immediate need was to edit the role which I got past with the last fix to #61. Delete would be nice but didn’t hold me back yet. I’ll retry and give you the full trace

@stgraham2000
Copy link
Author

My bad on the issue reporting- I'll edit the original issue because I had a copy/paste error (now wonder why you thought it was a duplicate). Sorry about that.

Here is the actual trace and not the copy from debug=True from the browser:

(0802ab) ERROR [MainProcess-40] log_response:228 django.request Internal Server Error: /admin/django_prbac/role/
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/options.py", line 607, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/sites.py", line 231, in inner
    return view(request, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/options.py", line 1704, in changelist_view
    response = self.response_action(request, queryset=cl.get_queryset(request))
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/options.py", line 1390, in response_action
    response = func(self, request, queryset)
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/actions.py", line 28, in delete_selected
    deletable_objects, model_count, perms_needed, protected = modeladmin.get_deleted_objects(queryset, request)
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/options.py", line 1826, in get_deleted_objects
    return get_deleted_objects(objs, request, self.admin_site)
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/utils.py", line 118, in get_deleted_objects
    collector.collect(objs)
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/utils.py", line 181, in collect
    return super().collect(objs, source_attr=source_attr, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/db/models/deletion.py", line 245, in collect
    field.remote_field.on_delete(self, field, sub_objs, self.using)
  File "/usr/local/lib/python3.7/site-packages/django/db/models/deletion.py", line 17, in CASCADE
    source_attr=field.name, nullable=field.null)
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/utils.py", line 179, in collect
    self.model_objs[obj._meta.model].add(obj)
TypeError: unhashable type: 'UserRole'```

@millerdev
Copy link
Contributor

Thanks @stgraham2000. Do you know if it is a new requirement with Django 3 that model classes must be hashable to support delete via admin interface? In other words, I wonder if this ever worked?

@stgraham2000
Copy link
Author

@millerdev, I just ran a simple test with 2.2.15 and I can delete roles. So to answer your question, it looks like it is a django 3+ issue. That said, another issue which we should probably raise is you can't add a new role in the admin if you leave the parameters blank. I'll quickly submit that issue as well with trace.

@mastnym
Copy link

mastnym commented Mar 12, 2021

I've discovered the same.

A class that overrides eq() and does not define hash() will have its hash() implicitly set to None. [source]

The solution would of course be, just to add a __hash__() method on Userrole, but here is a quick monkey patch:

def patch_hash(cls):
    __class__ = cls
    def hash(self):
       return super().__hash__()
    cls.__hash__ = hash
patch_hash(UserRole)

@mastnym
Copy link

mastnym commented Mar 12, 2021

One more thing that I discovered when using Django built in collector for cascade deletion. There should be a check in __eq__ method if the other model is also a UserRole first. Otherwise it will fail when comparing with other model's role and user attributes
And for some weird reason, if you have a Role connected to a UserRole this Role is not found by django collector whe deleting corresponding user. Cascade delete is set properly on UserRole model

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants