Repository: django-hijack/django-hijack

Issue:
Custom Proxy User Admin Throws Error
I have a project that started with the default django auth user. I then had to extend it, but since the project was already started, I did not want to update the `AUTH_USER_MODEL`. I set up a proxy user  model (my setup is actually here: https://stackoverflow.com/a/41683252/6719157) . 

In my admin.py, I am unsetting the User model from the site. I then add in my own DomainUserAdmin. This causes `UserModelAdmin = type(admin.site._registry[UserModel])` to throw a KeyError.


Discussion:
Hi @msopko81,

Thank you for reaching out. Yes, that does seem to be a bug. One of may, I presume, proxies are not well-supported in the Django world, but we should at least try. Do you happen to have an idea for a solution? If so, feel free to open a pull-request. If not, would you mind providing the full stack trace? This would make our lives much easier.

Best,
Joe

Resolving patch:
diff --git a/hijack/contrib/admin/apps.py b/hijack/contrib/admin/apps.py
--- a/hijack/contrib/admin/apps.py
+++ b/hijack/contrib/admin/apps.py
@@ -1,3 +1,5 @@
+import warnings
+
 from django.apps import AppConfig
 from django.contrib.auth import get_user_model
 
@@ -12,14 +14,24 @@ def ready(self):
         from . import HijackUserAdminMixin
 
         UserModel = get_user_model()
-        UserModelAdmin = type(admin.site._registry[UserModel])
-        admin.site.unregister(UserModel)
+        try:
+            UserModelAdmin = type(admin.site._registry[UserModel])
+        except KeyError:
+            # User model may not be registered, see also:
+            # https://github.com/django-hijack/django-hijack/issues/328
+            warnings.warn(
+                f"{UserModel.__qualname__} is not registered in the default admin."
+                f" Please integrate hijack into your user admin manually.",
+                UserWarning,
+            )
+        else:
+            admin.site.unregister(UserModel)
 
-        # We create a subclass including the HijackUserAdminMixin but keep the name
-        # and module, to keep output form failing checks consistent.
-        HijackUserModelAdmin = type(
-            UserModelAdmin.__name__, (HijackUserAdminMixin, UserModelAdmin), {}
-        )
-        HijackUserModelAdmin.__module__ = UserModelAdmin.__module__
+            # We create a subclass including the HijackUserAdminMixin but keep the name
+            # and module, to keep output form failing checks consistent.
+            HijackUserModelAdmin = type(
+                UserModelAdmin.__name__, (HijackUserAdminMixin, UserModelAdmin), {}
+            )
+            HijackUserModelAdmin.__module__ = UserModelAdmin.__module__
 
-        admin.site.register(UserModel, HijackUserModelAdmin)
+            admin.site.register(UserModel, HijackUserModelAdmin)


Test patch:
diff --git a/hijack/tests/test_admin.py b/hijack/tests/test_admin.py
--- a/hijack/tests/test_admin.py
+++ b/hijack/tests/test_admin.py
@@ -1,9 +1,11 @@
 from unittest.mock import MagicMock
 
+import pytest
 from django.urls import reverse
 
 from hijack.contrib.admin import HijackUserAdminMixin
-from hijack.tests.test_app.models import Post
+from hijack.contrib.admin.apps import HijackAdminConfig
+from hijack.tests.test_app.models import CustomUser, Post
 
 
 class TestHijackUserAdminMixin:
@@ -13,6 +15,18 @@ def test_user_admin(self, admin_client):
         assert response.status_code == 200
         assert b"data-hijack-user" in response.content
 
+    @pytest.fixture()
+    def no_user_admin(self, settings):
+        from django.contrib import admin
+
+        CustomUserAdmin = admin.site._registry.pop(CustomUser)
+        yield
+        admin.site._registry[CustomUser] = CustomUserAdmin
+
+    def test_user_admin__unregistered(self, no_user_admin, admin_client):
+        with pytest.warns(UserWarning, match="CustomUser is not registered"):
+            HijackAdminConfig.ready(None)
+
     def test_related_user(self, admin_client, admin_user):
         url = reverse("admin:test_app_post_changelist")
         Post.objects.create(author=admin_user)