chore: improve query performance#31
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces database query optimizations across several services and views, notably by prefetching organization settings and themes to prevent N+1 queries, and using bulk_create for policy generation. Feedback suggests avoiding the use of Django's private _prefetched_objects_cache in favor of the public all() API, simplifying the prefetch_related lookup in views.py to remove redundant Prefetch objects and unused imports, and returning the result of bulk_create directly to ensure primary keys are consistently populated.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| themes = getattr(setting, "_prefetched_objects_cache", {}).get("themes") | ||
| if themes is not None: | ||
| theme = next((item for item in themes if item.theme_key == theme_key), None) | ||
| else: | ||
| theme = setting.themes.filter(theme_key=theme_key).first() |
There was a problem hiding this comment.
Accessing Django's private _prefetched_objects_cache attribute is fragile and can break in future Django updates. Instead, you can use the public setting.themes.all() API. If the themes relation has been prefetched, Django will automatically use the prefetched cache without hitting the database. If it hasn't been prefetched, it will perform a query to fetch the themes. Since an organization setting typically has very few themes (e.g., light and dark), fetching all of them is highly efficient and much more maintainable.
| themes = getattr(setting, "_prefetched_objects_cache", {}).get("themes") | |
| if themes is not None: | |
| theme = next((item for item in themes if item.theme_key == theme_key), None) | |
| else: | |
| theme = setting.themes.filter(theme_key=theme_key).first() | |
| theme = next((item for item in setting.themes.all() if item.theme_key == theme_key), None) |
| queryset = OrganizationEmail.objects.select_related( | ||
| "organization", | ||
| "organization__organization_settings", | ||
| ).prefetch_related( | ||
| Prefetch( | ||
| "organization__organization_settings__themes", | ||
| queryset=OrganizationTheme.objects.all(), | ||
| ) | ||
| ) |
There was a problem hiding this comment.
Using Prefetch with a default queryset (OrganizationTheme.objects.all()) is redundant here. You can simplify the prefetch_related call by passing the lookup path as a string. This is cleaner, more readable, and avoids unnecessary imports.
queryset = OrganizationEmail.objects.select_related(
"organization",
"organization__organization_settings",
).prefetch_related(
"organization__organization_settings__themes"
)| from django.db.models import Prefetch | ||
| from django_filters.rest_framework import DjangoFilterBackend | ||
| from rest_framework.filters import OrderingFilter | ||
|
|
||
| from apps.custom_email.models import OrganizationEmail | ||
| from apps.custom_email.serializers import OrganizationEmailSerializer | ||
| from apps.organization_setting.models import OrganizationTheme |
There was a problem hiding this comment.
Since Prefetch and OrganizationTheme are no longer needed after simplifying the queryset, their imports can be safely removed to keep the file clean.
| from django.db.models import Prefetch | |
| from django_filters.rest_framework import DjangoFilterBackend | |
| from rest_framework.filters import OrderingFilter | |
| from apps.custom_email.models import OrganizationEmail | |
| from apps.custom_email.serializers import OrganizationEmailSerializer | |
| from apps.organization_setting.models import OrganizationTheme | |
| from django_filters.rest_framework import DjangoFilterBackend | |
| from rest_framework.filters import OrderingFilter | |
| from apps.custom_email.models import OrganizationEmail | |
| from apps.custom_email.serializers import OrganizationEmailSerializer |
| def create_default_policies(organization): | ||
| organization_policies = [] | ||
| for policy in default_policies: | ||
| organization_policy = OrganizationPolicy(**policy, organization=organization) | ||
| organization_policy.save() | ||
| organization_policies.append(organization_policy.pk) | ||
| organization_policies = [ | ||
| OrganizationPolicy(**policy, organization=organization) | ||
| for policy in default_policies | ||
| ] | ||
| OrganizationPolicy.objects.bulk_create(organization_policies) | ||
| return organization_policies |
There was a problem hiding this comment.
Django's bulk_create returns a list of the created objects with their primary keys populated (on supported databases like PostgreSQL). Returning the original organization_policies list directly is risky because the objects in the original list are not guaranteed to have their primary keys set in-place across all environments or database backends (e.g., during testing with SQLite). Returning the result of bulk_create ensures that the primary keys are always populated in the returned list.
| def create_default_policies(organization): | |
| organization_policies = [] | |
| for policy in default_policies: | |
| organization_policy = OrganizationPolicy(**policy, organization=organization) | |
| organization_policy.save() | |
| organization_policies.append(organization_policy.pk) | |
| organization_policies = [ | |
| OrganizationPolicy(**policy, organization=organization) | |
| for policy in default_policies | |
| ] | |
| OrganizationPolicy.objects.bulk_create(organization_policies) | |
| return organization_policies | |
| def create_default_policies(organization): | |
| organization_policies = [ | |
| OrganizationPolicy(**policy, organization=organization) | |
| for policy in default_policies | |
| ] | |
| return OrganizationPolicy.objects.bulk_create(organization_policies) |
What?
Improve the query performance
Why?
How?
Testing?
Anything Else?