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

Fixes: #17976 DeviceType_Count on Manufacturer #18583

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions netbox/utilities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
FieldDoesNotExist, FieldError, MultipleObjectsReturned, ObjectDoesNotExist, ValidationError,
)
from django.db.models.fields.related import ManyToOneRel, RelatedField
from django.db.models import Count, Prefetch
from django.urls import reverse
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -76,7 +77,7 @@ def get_view_name(view):
return drf_get_view_name(view)


def get_prefetches_for_serializer(serializer_class, fields_to_include=None):
def get_prefetches_for_serializer(serializer_class, fields_to_include=None, source_field=None):
"""
Compile and return a list of fields which should be prefetched on the queryset for a serializer.
"""
Expand All @@ -87,6 +88,7 @@ def get_prefetches_for_serializer(serializer_class, fields_to_include=None):
fields_to_include = serializer_class.Meta.fields

prefetch_fields = []
annotaded_prefetch = {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

minor spelling issue, annotated would be correct spelling

for field_name in fields_to_include:
serializer_field = serializer_class._declared_fields.get(field_name)

Expand All @@ -95,23 +97,37 @@ def get_prefetches_for_serializer(serializer_class, fields_to_include=None):
if serializer_field and serializer_field.source:
model_field_name = serializer_field.source

# If the serializer field is a RelatedObjectCountField and its a nested field
# Add an annotation to the annotaded_prefetch
if isinstance(serializer_field, RelatedObjectCountField) and source_field is not None:
if model_field_name not in annotaded_prefetch:
annotaded_prefetch[model_field_name] = Count(serializer_field.relation)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need to continue processing at this point? I think we can just do a continue here?


# If the serializer field does not map to a discrete model field, skip it.
try:
field = model._meta.get_field(model_field_name)
if isinstance(field, (RelatedField, ManyToOneRel, GenericForeignKey)):
if (isinstance(field, (RelatedField, ManyToOneRel, GenericForeignKey)) and
Copy link
Collaborator

Choose a reason for hiding this comment

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

This can be moved below the try/except - if the field doesn't exist we are doing a continue so it won't get hit and field will be set.
Also - if it is issubclass(type(serializer_field), Serializer) do we want to continue to line 117? Not sure - just asking, could we just do a check on that condition and continue?

not issubclass(type(serializer_field), Serializer)):
prefetch_fields.append(field.name)
except FieldDoesNotExist:
continue

# If this field is represented by a nested serializer, recurse to resolve prefetches
# for the related object.
if serializer_field:
if serializer_field and source_field is None:
if issubclass(type(serializer_field), Serializer):
# Determine which fields to prefetch for the nested object
subfields = serializer_field.Meta.brief_fields if serializer_field.nested else None
for subfield in get_prefetches_for_serializer(type(serializer_field), subfields):
prefetch_fields.append(f'{field_name}__{subfield}')

for subfield in get_prefetches_for_serializer(type(serializer_field), subfields, field_name):
if isinstance(subfield, Prefetch):
prefetch_fields.append(subfield)
else:
prefetch_fields.append(f'{field_name}__{subfield}')

# If there are annotaded_prefetch, add the annotaded prefetch to the prefetch_fields
if annotaded_prefetch:
related_prefetch = Prefetch(source_field, queryset=model.objects.all().annotate(**annotaded_prefetch))
prefetch_fields.append(related_prefetch)
return prefetch_fields


Expand Down