Skip to content

chore: improve query performance#31

Merged
ngovinh2k2 merged 1 commit into
devfrom
chore/improve-query-performance
Jul 3, 2026
Merged

chore: improve query performance#31
ngovinh2k2 merged 1 commit into
devfrom
chore/improve-query-performance

Conversation

@ngovinh2k2

Copy link
Copy Markdown
Member

What?

Improve the query performance

Why?

How?

Testing?

  • Functional Testing
  • Security
  • Performance
  • Error Handling
  • Code Quality
  • Documentation
  • Database
  • Deployment
  • Final Review

Anything Else?

@ngovinh2k2
ngovinh2k2 merged commit e4473e2 into dev Jul 3, 2026
1 check passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +20 to +24
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()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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)

Comment on lines +14 to +22
queryset = OrganizationEmail.objects.select_related(
"organization",
"organization__organization_settings",
).prefetch_related(
Prefetch(
"organization__organization_settings__themes",
queryset=OrganizationTheme.objects.all(),
)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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"
    )

Comment on lines +2 to +8
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since Prefetch and OrganizationTheme are no longer needed after simplifying the queryset, their imports can be safely removed to keep the file clean.

Suggested change
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

Comment on lines 110 to 116
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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)

@ngovinh2k2
ngovinh2k2 deleted the chore/improve-query-performance branch July 3, 2026 08:03
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

Successfully merging this pull request may close these issues.

1 participant