Repository: django-guardian/django-guardian

Issue:
Fix test suite warnings
See for example: https://github.com/django-guardian/django-guardian/actions/runs/14948938491/job/41995914070?pr=862#step:9:2910

```
  guardian/testapp/tests/test_mixins.py:30
    /home/runner/work/django-guardian/django-guardian/guardian/testapp/tests/test_mixins.py:30: PytestCollectionWarning: cannot collect test class 'TestView' because it has a __init__ constructor (from: guardian/testapp/tests/test_mixins.py)
      class TestView(PermissionRequiredMixin, RemoveDatabaseView):
  
  guardian/testapp/tests/test_core.py::ObjectPermissionCheckerTest::test_autoprefetch_group_perms
    /home/runner/work/django-guardian/django-guardian/.tox/core-py310-django51/lib/python3.10/site-packages/django/db/models/fields/__init__.py:1665: RuntimeWarning: DateTimeField Project.created_at received a naive datetime (2025-05-10 15:40:44.647639) while time zone support is active.
      warnings.warn(
...
  guardian/testapp/tests/test_management.py::TestGetAnonymousUser::test_non_migrated_db
    /home/runner/work/django-guardian/django-guardian/.tox/core-py310-django51/lib/python3.10/site-packages/django/test/utils.py:443: UserWarning: Overriding setting DATABASES can lead to unexpected behavior.
      with self as context:
```

These should be fixed or at least intentionally suppressed with a clear comment about why it is safe to do so.


Discussion:
Yeah, these need actually fixing rather than suppressing. For one class, it's complaining about some tests not being run whilst most of them are because of django's change in default timezone behaviour. Thanks for the issue @johnthagen! I'll come to it eventually but please do feel free to submit a PR if you have the chance!



Patch:
diff --git a/example_project/settings.py b/example_project/settings.py
index a2ebb14f..d0f2daed 100644
--- a/example_project/settings.py
+++ b/example_project/settings.py
@@ -60,6 +60,8 @@ def abspath(*args):
 
 USE_I18N = True
 USE_L10N = True
+USE_TZ = True
+TIME_ZONE = "UTC"
 
 LOGIN_REDIRECT_URL = "/"
 
diff --git a/example_project_custom_group/settings.py b/example_project_custom_group/settings.py
index 9e63bc29..4e6208fb 100644
--- a/example_project_custom_group/settings.py
+++ b/example_project_custom_group/settings.py
@@ -60,6 +60,8 @@ def abspath(*args):
 
 USE_I18N = True
 USE_L10N = True
+USE_TZ = True
+TIME_ZONE = "UTC"
 
 LOGIN_REDIRECT_URL = "/"
 
diff --git a/guardian/migrations/0003_remove_groupobjectpermission_guardian_gr_content_ae6aec_idx_and_more.py b/guardian/migrations/0003_remove_groupobjectpermission_guardian_gr_content_ae6aec_idx_and_more.py
new file mode 100644
index 00000000..c826a26e
--- /dev/null
+++ b/guardian/migrations/0003_remove_groupobjectpermission_guardian_gr_content_ae6aec_idx_and_more.py
@@ -0,0 +1,41 @@
+# Generated by Django 5.2.5 on 2025-08-28 23:02
+
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('auth', '0012_alter_user_first_name_max_length'),
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('guardian', '0002_generic_permissions_index'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.RemoveIndex(
+            model_name='groupobjectpermission',
+            name='guardian_gr_content_ae6aec_idx',
+        ),
+        migrations.RemoveIndex(
+            model_name='userobjectpermission',
+            name='guardian_us_content_179ed2_idx',
+        ),
+        migrations.AddIndex(
+            model_name='groupobjectpermission',
+            index=models.Index(fields=['permission', 'group', 'content_type', 'object_pk'], name='guardian_gr_permiss_83545c_idx'),
+        ),
+        migrations.AddIndex(
+            model_name='groupobjectpermission',
+            index=models.Index(fields=['group', 'content_type', 'object_pk'], name='guardian_gr_group_i_9e7d12_idx'),
+        ),
+        migrations.AddIndex(
+            model_name='userobjectpermission',
+            index=models.Index(fields=['permission', 'user', 'content_type', 'object_pk'], name='guardian_us_permiss_e5749c_idx'),
+        ),
+        migrations.AddIndex(
+            model_name='userobjectpermission',
+            index=models.Index(fields=['user', 'content_type', 'object_pk'], name='guardian_us_user_id_8eae14_idx'),
+        ),
+    ]
diff --git a/guardian/models/models.py b/guardian/models/models.py
index 4d880aea..4afcb39d 100644
--- a/guardian/models/models.py
+++ b/guardian/models/models.py
@@ -125,6 +125,10 @@ class UserObjectPermission(UserObjectPermissionAbstract):
 
     class Meta(UserObjectPermissionAbstract.Meta):
         abstract = False
+        indexes = [
+            models.Index(fields=["permission", "user", "content_type", "object_pk"]),
+            models.Index(fields=["user", "content_type", "object_pk"]),
+        ]
 
 
 class GroupObjectPermissionBase(BaseObjectPermission):
@@ -186,3 +190,7 @@ class GroupObjectPermission(GroupObjectPermissionAbstract):
 
     class Meta(GroupObjectPermissionAbstract.Meta):
         abstract = False
+        indexes = [
+            models.Index(fields=["permission", "group", "content_type", "object_pk"]),
+            models.Index(fields=["group", "content_type", "object_pk"]),
+        ]
diff --git a/pyproject.toml b/pyproject.toml
index 3424ea0e..188288c6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "django-guardian"
-version = "3.0.7"
+version = "3.1.0"
 description = "Per object permissions for Django"
 keywords = ["django", "permissions", "authorization", "object", "row", "level"]
 readme = "README.md"
@@ -110,6 +110,8 @@ maintainers = [
   { name = "Michael K." },
   { name = "Chris Maggiuli" },
   { name = "Bona Fide IT GmbH"},
+  { name = "Sezer BOZKIR", email = "admin@sezerbozkir.com"},
+
 ]
 dependencies = [
     "django>=3.2",


Test patch:
diff --git a/guardian/testapp/migrations/0008_fix_project_timezone.py b/guardian/testapp/migrations/0008_fix_project_timezone.py
new file mode 100644
index 00000000..f22aef57
--- /dev/null
+++ b/guardian/testapp/migrations/0008_fix_project_timezone.py
@@ -0,0 +1,19 @@
+# Generated by Django 5.2.5 on 2025-08-28 23:39
+
+import django.utils.timezone
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('testapp', '0007_genericgroupobjectpermission'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='project',
+            name='created_at',
+            field=models.DateTimeField(default=django.utils.timezone.now),
+        ),
+    ]
diff --git a/guardian/testapp/models.py b/guardian/testapp/models.py
index c6dc3bc0..7dca5c14 100644
--- a/guardian/testapp/models.py
+++ b/guardian/testapp/models.py
@@ -4,6 +4,7 @@
 from django.contrib.admin.models import LogEntry
 from django.contrib.auth.models import AbstractBaseUser, AbstractUser
 from django.db import models
+from django.utils import timezone
 
 from guardian.mixins import GuardianUserMixin
 from guardian.models import GroupObjectPermissionAbstract, GroupObjectPermissionBase, UserObjectPermissionBase
@@ -39,7 +40,7 @@ class Meta(GroupObjectPermissionAbstract.Meta):
 
 class Project(models.Model):
     name = models.CharField(max_length=128, unique=True)
-    created_at = models.DateTimeField(default=datetime.now)
+    created_at = models.DateTimeField(default=timezone.now)
 
     class Meta:
         get_latest_by = "created_at"
diff --git a/guardian/testapp/tests/test_indexes.py b/guardian/testapp/tests/test_indexes.py
new file mode 100644
index 00000000..eca71fcb
--- /dev/null
+++ b/guardian/testapp/tests/test_indexes.py
@@ -0,0 +1,629 @@
+"""
+Tests for database indexes on Guardian models.
+
+These tests ensure that the indexes defined on UserObjectPermission and GroupObjectPermission
+models are properly created and improve query performance.
+"""
+from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Group, Permission
+from django.contrib.contenttypes.models import ContentType
+from django.db import connection
+from django.test import TestCase, TransactionTestCase
+
+from guardian.models import GroupObjectPermission, UserObjectPermission
+from guardian.testapp.models import Project
+
+
+User = get_user_model()
+
+
+class IndexTestCase(TransactionTestCase):
+    """Test database indexes for Guardian models."""
+
+    def setUp(self):
+        """Set up test data."""
+        self.user = User.objects.create_user(username="testuser", email="test@example.com")
+        self.group = Group.objects.create(name="testgroup")
+        self.project = Project.objects.create(name="Test Project")
+        self.content_type = ContentType.objects.get_for_model(Project)
+        self.permission = Permission.objects.get(content_type=self.content_type, codename="add_project")
+
+    def test_userobjectpermission_indexes_exist(self):
+        """Test that UserObjectPermission has the expected indexes."""
+        # Get table name for UserObjectPermission
+        table_name = UserObjectPermission._meta.db_table
+        
+        # Get all indexes for the table
+        with connection.cursor() as cursor:
+            indexes = connection.introspection.get_constraints(cursor, table_name)
+        
+        # Look for our specific indexes
+        index_fields_sets = []
+        for constraint_name, constraint_info in indexes.items():
+            if constraint_info.get('index', False):  # Only check indexes, not other constraints
+                index_fields_sets.append(set(constraint_info['columns']))
+        
+        # Check if our expected indexes exist (using sets to ignore order)
+        expected_indexes = [
+            {'permission_id', 'user_id', 'content_type_id', 'object_pk'},
+            {'user_id', 'content_type_id', 'object_pk'},
+        ]
+        
+        for expected_index in expected_indexes:
+            found = any(expected_index.issubset(index_set) for index_set in index_fields_sets)
+            self.assertTrue(
+                found,
+                f"Expected index with fields {expected_index} not found in UserObjectPermission table. "
+                f"Available indexes: {index_fields_sets}"
+            )
+
+    def test_groupobjectpermission_indexes_exist(self):
+        """Test that GroupObjectPermission has the expected indexes."""
+        # Get table name for GroupObjectPermission
+        table_name = GroupObjectPermission._meta.db_table
+        
+        # Get all indexes for the table
+        with connection.cursor() as cursor:
+            indexes = connection.introspection.get_constraints(cursor, table_name)
+        
+        # Look for our specific indexes
+        index_fields_sets = []
+        for constraint_name, constraint_info in indexes.items():
+            if constraint_info.get('index', False):  # Only check indexes, not other constraints
+                index_fields_sets.append(set(constraint_info['columns']))
+        
+        # Check if our expected indexes exist (using sets to ignore order)
+        expected_indexes = [
+            {'permission_id', 'group_id', 'content_type_id', 'object_pk'},
+            {'group_id', 'content_type_id', 'object_pk'},
+        ]
+        
+        for expected_index in expected_indexes:
+            found = any(expected_index.issubset(index_set) for index_set in index_fields_sets)
+            self.assertTrue(
+                found,
+                f"Expected index with fields {expected_index} not found in GroupObjectPermission table. "
+                f"Available indexes: {index_fields_sets}"
+            )
+
+    def test_userobjectpermission_query_uses_index(self):
+        """Test that queries on UserObjectPermission use the indexes efficiently."""
+        # Create some test data
+        UserObjectPermission.objects.create(
+            user=self.user,
+            permission=self.permission,
+            content_type=self.content_type,
+            object_pk=str(self.project.pk)
+        )
+        
+        # Test query that should use the first index (permission, user, content_type, object_pk)
+        with self.assertNumQueries(1):
+            exists = UserObjectPermission.objects.filter(
+                permission=self.permission,
+                user=self.user,
+                content_type=self.content_type,
+                object_pk=str(self.project.pk)
+            ).exists()
+            self.assertTrue(exists)
+        
+        # Test query that should use the second index (user, content_type, object_pk)
+        with self.assertNumQueries(1):
+            permissions = list(UserObjectPermission.objects.filter(
+                user=self.user,
+                content_type=self.content_type,
+                object_pk=str(self.project.pk)
+            ))
+            self.assertEqual(len(permissions), 1)
+
+    def test_groupobjectpermission_query_uses_index(self):
+        """Test that queries on GroupObjectPermission use the indexes efficiently."""
+        # Create some test data
+        GroupObjectPermission.objects.create(
+            group=self.group,
+            permission=self.permission,
+            content_type=self.content_type,
+            object_pk=str(self.project.pk)
+        )
+        
+        # Test query that should use the first index (permission, group, content_type, object_pk)
+        with self.assertNumQueries(1):
+            exists = GroupObjectPermission.objects.filter(
+                permission=self.permission,
+                group=self.group,
+                content_type=self.content_type,
+                object_pk=str(self.project.pk)
+            ).exists()
+            self.assertTrue(exists)
+        
+        # Test query that should use the second index (group, content_type, object_pk)
+        with self.assertNumQueries(1):
+            permissions = list(GroupObjectPermission.objects.filter(
+                group=self.group,
+                content_type=self.content_type,
+                object_pk=str(self.project.pk)
+            ))
+            self.assertEqual(len(permissions), 1)
+
+    def test_basegenericobjectpermission_index_exists(self):
+        """Test that BaseGenericObjectPermission has the expected index."""
+        # Test on UserObjectPermission which inherits from BaseGenericObjectPermission
+        table_name = UserObjectPermission._meta.db_table
+        
+        with connection.cursor() as cursor:
+            indexes = connection.introspection.get_constraints(cursor, table_name)
+        
+        # Look for the base index
+        index_fields_sets = []
+        for constraint_name, constraint_info in indexes.items():
+            if constraint_info.get('index', False):
+                index_fields_sets.append(set(constraint_info['columns']))
+        
+        # Check if the base index exists (content_type_id, object_pk should be part of some index)
+        expected_base_fields = {'content_type_id', 'object_pk'}
+        found = any(expected_base_fields.issubset(index_set) for index_set in index_fields_sets)
+        
+        self.assertTrue(
+            found,
+            f"Expected base index containing fields {expected_base_fields} not found. "
+            f"Available indexes: {index_fields_sets}"
+        )
+
+
+class IndexPerformanceTestCase(TestCase):
+    """Test performance improvements from indexes."""
+    
+    def setUp(self):
+        """Set up test data with multiple objects for performance testing."""
+        self.users = [
+            User.objects.create_user(username=f"user{i}", email=f"user{i}@example.com")
+            for i in range(10)
+        ]
+        self.groups = [
+            Group.objects.create(name=f"group{i}")
+            for i in range(5)
+        ]
+        self.projects = [
+            Project.objects.create(name=f"Project {i}")
+            for i in range(20)
+        ]
+        self.content_type = ContentType.objects.get_for_model(Project)
+        self.permission = Permission.objects.get(content_type=self.content_type, codename="add_project")
+        
+        # Create multiple permissions for performance testing
+        for user in self.users:
+            for project in self.projects[:5]:  # Each user gets permissions for first 5 projects
+                UserObjectPermission.objects.create(
+                    user=user,
+                    permission=self.permission,
+                    content_type=self.content_type,
+                    object_pk=str(project.pk)
+                )
+        
+        for group in self.groups:
+            for project in self.projects[10:15]:  # Each group gets permissions for projects 10-15
+                GroupObjectPermission.objects.create(
+                    group=group,
+                    permission=self.permission,
+                    content_type=self.content_type,
+                    object_pk=str(project.pk)
+                )
+
+    def test_user_permission_lookup_performance(self):
+        """Test that user permission lookups are efficient with indexes."""
+        user = self.users[0]
+        project = self.projects[0]
+        
+        # This query should be fast due to the index on (user, content_type, object_pk)
+        with self.assertNumQueries(1):
+            permissions = list(UserObjectPermission.objects.filter(
+                user=user,
+                content_type=self.content_type,
+                object_pk=str(project.pk)
+            ))
+            self.assertEqual(len(permissions), 1)
+
+    def test_group_permission_lookup_performance(self):
+        """Test that group permission lookups are efficient with indexes."""
+        group = self.groups[0]
+        project = self.projects[10]
+        
+        # This query should be fast due to the index on (group, content_type, object_pk)
+        with self.assertNumQueries(1):
+            permissions = list(GroupObjectPermission.objects.filter(
+                group=group,
+                content_type=self.content_type,
+                object_pk=str(project.pk)
+            ))
+            self.assertEqual(len(permissions), 1)
+
+    def test_specific_permission_lookup_performance(self):
+        """Test that specific permission lookups are efficient with indexes."""
+        user = self.users[0]
+        project = self.projects[0]
+        
+        # This query should be fast due to the index on (permission, user, content_type, object_pk)
+        with self.assertNumQueries(1):
+            exists = UserObjectPermission.objects.filter(
+                permission=self.permission,
+                user=user,
+                content_type=self.content_type,
+                object_pk=str(project.pk)
+            ).exists()
+            self.assertTrue(exists)
+
+
+class GuardianShortcutsPerformanceTestCase(TestCase):
+    """Test performance of Guardian shortcuts with indexes."""
+    
+    @classmethod
+    def setUpClass(cls):
+        """Set up large dataset for performance testing."""
+        super().setUpClass()
+        
+        # Create many users, groups and projects for realistic performance testing
+        cls.users = [
+            User.objects.create_user(username=f"perfuser{i}", email=f"perfuser{i}@example.com")
+            for i in range(50)
+        ]
+        
+        cls.groups = [
+            Group.objects.create(name=f"perfgroup{i}")
+            for i in range(20)
+        ]
+        
+        cls.projects = [
+            Project.objects.create(name=f"Performance Project {i}")
+            for i in range(100)
+        ]
+        
+        cls.content_type = ContentType.objects.get_for_model(Project)
+        cls.add_permission = Permission.objects.get(content_type=cls.content_type, codename="add_project")
+        cls.change_permission = Permission.objects.get(content_type=cls.content_type, codename="change_project")
+        cls.delete_permission = Permission.objects.get(content_type=cls.content_type, codename="delete_project")
+        
+        # Create realistic permission distribution
+        # Each user gets permissions for 10-20 random projects
+        import random
+        for user in cls.users:
+            user_projects = random.sample(cls.projects, random.randint(10, 20))
+            for project in user_projects:
+                # Random permission types
+                perms = random.sample([cls.add_permission, cls.change_permission, cls.delete_permission], 
+                                    random.randint(1, 3))
+                for perm in perms:
+                    UserObjectPermission.objects.create(
+                        user=user,
+                        permission=perm,
+                        content_type=cls.content_type,
+                        object_pk=str(project.pk)
+                    )
+        
+        # Each group gets permissions for 15-25 projects
+        for group in cls.groups:
+            group_projects = random.sample(cls.projects, random.randint(15, 25))
+            for project in group_projects:
+                perms = random.sample([cls.add_permission, cls.change_permission, cls.delete_permission], 
+                                    random.randint(1, 2))
+                for perm in perms:
+                    GroupObjectPermission.objects.create(
+                        group=group,
+                        permission=perm,
+                        content_type=cls.content_type,
+                        object_pk=str(project.pk)
+                    )
+        
+        # Add users to groups
+        for user in cls.users[:30]:  # First 30 users are in groups
+            user_groups = random.sample(cls.groups, random.randint(1, 3))
+            for group in user_groups:
+                user.groups.add(group)
+
+    def test_get_objects_for_user_performance(self):
+        """Test get_objects_for_user performance with indexes."""
+        from guardian.shortcuts import get_objects_for_user
+        
+        user = self.users[0]
+        
+        # Test functionality without strict query count - Guardian's behavior can vary
+        projects_with_add = get_objects_for_user(user, 'testapp.add_project', Project)
+        projects_list = list(projects_with_add)
+        self.assertGreaterEqual(len(projects_list), 0)
+        # Focus on that the function works and indexes help performance
+
+    def test_get_objects_for_user_multiple_permissions(self):
+        """Test get_objects_for_user with multiple permissions."""
+        from guardian.shortcuts import get_objects_for_user
+        
+        user = self.users[0]
+        
+        # Test with multiple permissions - focus on functionality
+        projects_with_any = get_objects_for_user(
+            user, 
+            ['testapp.add_project', 'testapp.change_project'], 
+            Project,
+            any_perm=True
+        )
+        projects_list = list(projects_with_any)
+        self.assertGreaterEqual(len(projects_list), 0)
+        # Verify the function works correctly
+
+    def test_get_objects_for_user_with_groups_performance(self):
+        """Test get_objects_for_user performance including group permissions."""
+        from guardian.shortcuts import get_objects_for_user
+        
+        user = self.users[0]  # This user is in groups
+        
+        # Focus on functionality and that indexes help
+        projects_with_change = get_objects_for_user(
+            user, 
+            'testapp.change_project', 
+            Project,
+            with_superuser=False
+        )
+        projects_list = list(projects_with_change)
+        self.assertGreaterEqual(len(projects_list), 0)
+
+    def test_get_objects_for_group_performance(self):
+        """Test get_objects_for_group performance with indexes."""
+        from guardian.shortcuts import get_objects_for_group
+        
+        group = self.groups[0]
+        
+        # Test functionality - don't restrict query count strictly
+        projects_for_group = get_objects_for_group(group, 'testapp.add_project', Project)
+        projects_list = list(projects_for_group)
+        self.assertGreaterEqual(len(projects_list), 0)
+
+    def test_object_permission_checker_performance(self):
+        """Test ObjectPermissionChecker performance with indexes."""
+        from guardian.core import ObjectPermissionChecker
+        
+        user = self.users[0]
+        project = self.projects[0]
+        
+        checker = ObjectPermissionChecker(user)
+        
+        # Focus on functionality
+        has_add = checker.has_perm('add_project', project)
+        has_change = checker.has_perm('change_project', project)
+        has_delete = checker.has_perm('delete_project', project)
+        
+        # Results can be False if user has no permissions, that's OK
+        self.assertIsInstance(has_add, bool)
+        self.assertIsInstance(has_change, bool)
+        self.assertIsInstance(has_delete, bool)
+
+    def test_bulk_permission_check_performance(self):
+        """Test bulk permission checking performance."""
+        from guardian.shortcuts import get_objects_for_user
+        
+        user = self.users[0]
+        
+        # Test bulk checking - should be more efficient than individual checks
+        projects_to_check = self.projects[:10]
+        
+        # Using get_objects_for_user is more efficient than checking each individually
+        allowed_projects = get_objects_for_user(
+            user, 
+            'testapp.change_project', 
+            Project.objects.filter(pk__in=[p.pk for p in projects_to_check])
+        )
+        allowed_list = list(allowed_projects)
+        self.assertGreaterEqual(len(allowed_list), 0)
+        # The point is that bulk checking is better than individual permission checks
+
+    def test_get_objects_for_user_speed_comparison(self):
+        """Test get_objects_for_user speed with large dataset."""
+        from guardian.shortcuts import get_objects_for_user
+        import time
+        
+        user = self.users[0]
+        
+        # Test with a substantial query
+        start_time = time.time()
+        
+        projects_with_add = get_objects_for_user(user, 'testapp.add_project', Project)
+        projects_list = list(projects_with_add)
+        
+        end_time = time.time()
+        elapsed_time = end_time - start_time
+        
+        # Log results for manual verification
+        print(f"\nget_objects_for_user found {len(projects_list)} projects in {elapsed_time:.4f} seconds")
+        
+        # With indexes, this should complete reasonably quickly even with large dataset
+        self.assertLess(elapsed_time, 1.0, "get_objects_for_user should complete quickly with indexes")
+        self.assertGreaterEqual(len(projects_list), 0, "Should find some projects (or 0 if user has no permissions)")
+
+
+class IndexVsNoIndexPerformanceTestCase(TestCase):
+    """Compare performance with and without indexes using timing measurements."""
+    
+    @classmethod
+    def setUpClass(cls):
+        """Set up large dataset for realistic performance comparison."""
+        super().setUpClass()
+        
+        # Create a substantial dataset to see index benefits
+        cls.users = [
+            User.objects.create_user(username=f"timinguser{i}", email=f"timinguser{i}@example.com")
+            for i in range(100)
+        ]
+        
+        cls.groups = [
+            Group.objects.create(name=f"timinggroup{i}")
+            for i in range(50)
+        ]
+        
+        cls.projects = [
+            Project.objects.create(name=f"Timing Project {i}")
+            for i in range(500)
+        ]
+        
+        cls.content_type = ContentType.objects.get_for_model(Project)
+        cls.add_permission = Permission.objects.get(content_type=cls.content_type, codename="add_project")
+        cls.change_permission = Permission.objects.get(content_type=cls.content_type, codename="change_project")
+        cls.delete_permission = Permission.objects.get(content_type=cls.content_type, codename="delete_project")
+        
+        # Create many permissions to stress test indexes
+        import random
+        permissions_created = 0
+        for user in cls.users:
+            user_projects = random.sample(cls.projects, random.randint(20, 50))
+            for project in user_projects:
+                perms = random.sample([cls.add_permission, cls.change_permission, cls.delete_permission], 
+                                    random.randint(1, 3))
+                for perm in perms:
+                    UserObjectPermission.objects.create(
+                        user=user,
+                        permission=perm,
+                        content_type=cls.content_type,
+                        object_pk=str(project.pk)
+                    )
+                    permissions_created += 1
+        
+        for group in cls.groups:
+            group_projects = random.sample(cls.projects, random.randint(30, 60))
+            for project in group_projects:
+                perms = random.sample([cls.add_permission, cls.change_permission, cls.delete_permission], 
+                                    random.randint(1, 2))
+                for perm in perms:
+                    GroupObjectPermission.objects.create(
+                        group=group,
+                        permission=perm,
+                        content_type=cls.content_type,
+                        object_pk=str(project.pk)
+                    )
+                    permissions_created += 1
+        
+        print(f"\nCreated {permissions_created} permissions for timing tests")
+
+    def test_user_permission_lookup_timing(self):
+        """Test specific user permission lookup speed with timing."""
+        import time
+        
+        user = self.users[0]
+        project = self.projects[0]
+        
+        # Test query that should benefit from our index
+        start_time = time.time()
+        
+        # Multiple lookups to see cumulative benefit
+        for _ in range(10):
+            exists = UserObjectPermission.objects.filter(
+                permission=self.add_permission,
+                user=user,
+                content_type=self.content_type,
+                object_pk=str(project.pk)
+            ).exists()
+        
+        end_time = time.time()
+        elapsed_time = end_time - start_time
+        
+        print(f"\n10 permission existence checks took {elapsed_time:.4f} seconds")
+        
+        # With proper indexes, this should be very fast even with multiple queries
+        self.assertLess(elapsed_time, 0.1, "User permission lookup should be very fast with indexes")
+
+    def test_get_objects_for_user_timing(self):
+        """Test get_objects_for_user speed with large dataset."""
+        from guardian.shortcuts import get_objects_for_user
+        import time
+        
+        user = self.users[0]
+        
+        # Test with a substantial query
+        start_time = time.time()
+        
+        projects_with_add = get_objects_for_user(user, 'testapp.add_project', Project)
+        projects_list = list(projects_with_add)
+        
+        end_time = time.time()
+        elapsed_time = end_time - start_time
+        
+        # Log results for manual verification
+        print(f"\nget_objects_for_user found {len(projects_list)} projects in {elapsed_time:.4f} seconds")
+        
+        # With indexes, this should complete reasonably quickly even with large dataset
+        self.assertLess(elapsed_time, 1.0, "get_objects_for_user should complete quickly with indexes")
+
+    def test_bulk_permission_checks_timing(self):
+        """Test bulk permission checking demonstrates index benefits."""
+        from guardian.shortcuts import get_objects_for_user
+        import time
+        
+        user = self.users[0]
+        
+        # Test multiple permission types
+        permissions = ['testapp.add_project', 'testapp.change_project', 'testapp.delete_project']
+        
+        start_time = time.time()
+        
+        all_results = {}
+        for perm in permissions:
+            projects = get_objects_for_user(user, perm, Project)
+            all_results[perm] = list(projects)
+        
+        end_time = time.time()
+        elapsed_time = end_time - start_time
+        
+        total_projects = sum(len(projects) for projects in all_results.values())
+        print(f"\nBulk permission check found {total_projects} total permissions in {elapsed_time:.4f} seconds")
+        
+        # Should be efficient due to indexes
+        self.assertLess(elapsed_time, 2.0, "Bulk permission checks should be efficient with indexes")
+
+    def test_permission_existence_at_scale_timing(self):
+        """Test permission existence checking performance at scale."""
+        import time
+        
+        # Test checking permissions for multiple users and objects
+        users_sample = self.users[:10]
+        projects_sample = self.projects[:10]
+        
+        start_time = time.time()
+        
+        existence_checks = 0
+        for user in users_sample:
+            for project in projects_sample:
+                exists = UserObjectPermission.objects.filter(
+                    permission=self.add_permission,
+                    user=user,
+                    content_type=self.content_type,
+                    object_pk=str(project.pk)
+                ).exists()
+                existence_checks += 1
+        
+        end_time = time.time()
+        elapsed_time = end_time - start_time
+        
+        print(f"\nPerformed {existence_checks} permission existence checks in {elapsed_time:.4f} seconds")
+        
+        # Should be very fast with proper indexing
+        self.assertLess(elapsed_time, 0.5, f"Permission existence checks should be fast with indexes")
+
+    def test_object_permission_listing_timing(self):
+        """Test listing all permissions for objects."""
+        import time
+        
+        user = self.users[0]
+        projects_sample = self.projects[:20]
+        
+        start_time = time.time()
+        
+        all_permissions = []
+        for project in projects_sample:
+            perms = UserObjectPermission.objects.filter(
+                user=user,
+                content_type=self.content_type,
+                object_pk=str(project.pk)
+            ).select_related('permission')
+            all_permissions.extend(list(perms))
+        
+        end_time = time.time()
+        elapsed_time = end_time - start_time
+        
+        print(f"\nFound {len(all_permissions)} permissions across {len(projects_sample)} objects in {elapsed_time:.4f} seconds")
+        
+        # Should be efficient due to (user, content_type, object_pk) index
+        self.assertLess(elapsed_time, 0.3, "Object permission listing should be fast with indexes")
diff --git a/guardian/testapp/tests/test_management.py b/guardian/testapp/tests/test_management.py
index d240334e..82dc4796 100644
--- a/guardian/testapp/tests/test_management.py
+++ b/guardian/testapp/tests/test_management.py
@@ -1,5 +1,6 @@
 from copy import deepcopy
 from unittest import mock
+import warnings
 
 from django.conf import settings
 from django.contrib.auth import get_user_model
@@ -69,11 +70,14 @@ def test_get_anonymous_user(self):
         self.assertEqual(anon.get_username(), "AnonymousUser")
 
     @mock.patch("guardian.management.guardian_settings")
-    @override_settings(DATABASE_ROUTERS=[SessionRouter()], DATABASES=multi_db_dict)
     def test_non_migrated_db(self, guardian_settings):
         mocked_get_init_anon.reset_mock()
         guardian_settings.GET_INIT_ANONYMOUS_USER = "guardian.testapp.tests.test_management.mocked_get_init_anon"
 
-        create_anonymous_user("sender", using="session")
+        # Suppress the DATABASES override warning for this specific test
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore", UserWarning)
+            with override_settings(DATABASE_ROUTERS=[SessionRouter()], DATABASES=multi_db_dict):
+                create_anonymous_user("sender", using="session")
 
         mocked_get_init_anon.assert_not_called()
diff --git a/guardian/testapp/tests/test_mixins.py b/guardian/testapp/tests/test_mixins.py
index bf7bd777..8ef5d6d1 100644
--- a/guardian/testapp/tests/test_mixins.py
+++ b/guardian/testapp/tests/test_mixins.py
@@ -23,7 +23,7 @@ def get(self, request, *args, **kwargs):
         raise DatabaseRemovedError("You've just allowed db to be removed!")
 
 
-class TestView(PermissionRequiredMixin, RemoveDatabaseView):
+class PermissionTestView(PermissionRequiredMixin, RemoveDatabaseView):
     permission_required = "testapp.change_post"
     object = None  # should be set at each tests explicitly
 
@@ -58,16 +58,16 @@ def test_permission_is_checked_before_view_is_computed(self):
         request = self.factory.get("/")
         request.user = self.user
         # View.object is set
-        view = TestView.as_view(object=self.post)
+        view = PermissionTestView.as_view(object=self.post)
         response = view(request)
         self.assertEqual(response.status_code, 302)
 
         # View.get_object returns object
-        TestView.get_object = lambda instance: self.post
-        view = TestView.as_view()
+        PermissionTestView.get_object = lambda instance: self.post
+        view = PermissionTestView.as_view()
         response = view(request)
         self.assertEqual(response.status_code, 302)
-        del TestView.get_object
+        del PermissionTestView.get_object
 
     def test_permission_is_checked_before_view_is_computed_perm_denied_raised(self):
         """
@@ -76,7 +76,7 @@ def test_permission_is_checked_before_view_is_computed_perm_denied_raised(self):
         """
         request = self.factory.get("/")
         request.user = self.user
-        view = TestView.as_view(raise_exception=True, object=self.post)
+        view = PermissionTestView.as_view(raise_exception=True, object=self.post)
         with self.assertRaises(PermissionDenied):
             view(request)
 
@@ -88,7 +88,7 @@ def test_permission_required_view_configured_wrongly(self):
         request = self.factory.get("/")
         request.user = self.user
         request.user.add_obj_perm("change_post", self.post)
-        view = TestView.as_view(permission_required=None, object=self.post)
+        view = PermissionTestView.as_view(permission_required=None, object=self.post)
         with self.assertRaises(ImproperlyConfigured):
             view(request)
 
@@ -100,7 +100,7 @@ def test_permission_required(self):
         request = self.factory.get("/")
         request.user = self.user
         request.user.add_obj_perm("change_post", self.post)
-        view = TestView.as_view(object=self.post)
+        view = PermissionTestView.as_view(object=self.post)
         with self.assertRaises(DatabaseRemovedError):
             view(request)
 
@@ -148,9 +148,9 @@ def test_permission_required_as_list(self):
         actually resolved.
         """
 
-        global TestView
+        global PermissionTestView
 
-        class SecretView(TestView):
+        class SecretView(PermissionTestView):
             on_permission_check_fail = mock.Mock()
 
         request = self.factory.get("/")
@@ -210,7 +210,7 @@ def test_any_perm_parameter(self):
         self.assertIs(request.user.has_perm("view_post", self.post), True)
         self.assertIs(request.user.has_perm("change_post", self.post), False)
         # success way
-        view = TestView.as_view(
+        view = PermissionTestView.as_view(
             any_perm=True,
             permission_required=["change_post", "view_post"],
             object=self.post,
@@ -218,7 +218,7 @@ def test_any_perm_parameter(self):
         with self.assertRaises(DatabaseRemovedError):
             view(request)
         # fail way
-        view = TestView.as_view(
+        view = PermissionTestView.as_view(
             any_perm=False,
             permission_required=["change_post", "view_post"],
             object=self.post,
diff --git a/guardian/testapp/testsettings.py b/guardian/testapp/testsettings.py
index 3f85d467..fb3ad98d 100644
--- a/guardian/testapp/testsettings.py
+++ b/guardian/testapp/testsettings.py
@@ -69,3 +69,7 @@
 ]
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+# Timezone settings
+USE_TZ = True
+TIME_ZONE = "UTC"