Skip to content

20089 use get_queryset function for valid_models #20090

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

Merged
merged 1 commit into from
Aug 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion netbox/core/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
router.register('data-sources', views.DataSourceViewSet)
router.register('data-files', views.DataFileViewSet)
router.register('jobs', views.JobViewSet)
router.register('object-changes', views.ObjectChangeViewSet)
router.register('object-changes', views.ObjectChangeViewSet, basename='objectchange')
Copy link
Member

Choose a reason for hiding this comment

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

Why set basename?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When you remove queryset (change to function) from API view it throws an error unless basename is set.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I looked at trying to move to Q objects, like the following, however the issue is get_models returns a dict and going like this still causes the issue where the content types are only resolved at instantiation. As discussed an alternative solution might be to put it into the housekeeping task or such but that would be a breaking change:

    def valid_models(self):
        # Exclude any change records which refer to an instance of a model that's no longer installed. This
        # can happen when a plugin is removed but its data remains in the database, for example.
        from django.db.models import Q
        
        # Get all currently registered models at query time
        current_models = apps.get_models()
        
        # Build a Q object that matches any ContentType that corresponds to a currently registered model
        valid_content_type_conditions = Q()
        for model in current_models:
            valid_content_type_conditions |= Q(
                changed_object_type__app_label=model._meta.app_label,
                changed_object_type__model=model._meta.model_name
            )
        
        return self.filter(valid_content_type_conditions)

router.register('object-types', views.ObjectTypeViewSet)
router.register('background-queues', views.BackgroundQueueViewSet, basename='rqqueue')
router.register('background-workers', views.BackgroundWorkerViewSet, basename='rqworker')
Expand Down
4 changes: 3 additions & 1 deletion netbox/core/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,12 @@ class ObjectChangeViewSet(ReadOnlyModelViewSet):
Retrieve a list of recent changes.
"""
metadata_class = ContentTypeMetadata
queryset = ObjectChange.objects.valid_models()
serializer_class = serializers.ObjectChangeSerializer
filterset_class = filtersets.ObjectChangeFilterSet

def get_queryset(self):
return ObjectChange.objects.valid_models()


class ObjectTypeViewSet(ReadOnlyModelViewSet):
"""
Expand Down
10 changes: 8 additions & 2 deletions netbox/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,17 +216,23 @@ class JobBulkDeleteView(generic.BulkDeleteView):

@register_model_view(ObjectChange, 'list', path='', detail=False)
class ObjectChangeListView(generic.ObjectListView):
queryset = ObjectChange.objects.valid_models()
queryset = None
filterset = filtersets.ObjectChangeFilterSet
filterset_form = forms.ObjectChangeFilterForm
table = tables.ObjectChangeTable
template_name = 'core/objectchange_list.html'
actions = (BulkExport,)

def get_queryset(self, request):
return ObjectChange.objects.valid_models()


@register_model_view(ObjectChange)
class ObjectChangeView(generic.ObjectView):
queryset = ObjectChange.objects.valid_models()
queryset = None

def get_queryset(self, request):
return ObjectChange.objects.valid_models()

def get_extra_context(self, request, instance):
related_changes = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(
Expand Down