1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Fixes #11335: Default manager for ObjectChange should filter by installed apps (#11709)

* Fixes #11335: Default manager for ObjectChange should filter by installed apps

* Employ canonical model discovery mechanism

* Move filtering logic to valid_models() queryset method

* fixed import to avoid content type does not exist

* Cleanup

---------

Co-authored-by: Abhimanyu Saharan <desk.abhimanyu@gmail.com>
This commit is contained in:
Jeremy Stretch
2023-07-05 11:39:35 -04:00
committed by GitHub
parent 3307bd200c
commit 63ba9fb38c
6 changed files with 25 additions and 9 deletions

View File

@ -368,7 +368,7 @@ class ObjectChangeViewSet(ReadOnlyModelViewSet):
Retrieve a list of recent changes. Retrieve a list of recent changes.
""" """
metadata_class = ContentTypeMetadata metadata_class = ContentTypeMetadata
queryset = ObjectChange.objects.prefetch_related('user') queryset = ObjectChange.objects.valid_models().prefetch_related('user')
serializer_class = serializers.ObjectChangeSerializer serializer_class = serializers.ObjectChangeSerializer
filterset_class = filtersets.ObjectChangeFilterSet filterset_class = filtersets.ObjectChangeFilterSet

View File

@ -5,7 +5,7 @@ from django.db import models
from django.urls import reverse from django.urls import reverse
from extras.choices import * from extras.choices import *
from utilities.querysets import RestrictedQuerySet from ..querysets import ObjectChangeQuerySet
__all__ = ( __all__ = (
'ObjectChange', 'ObjectChange',
@ -82,7 +82,7 @@ class ObjectChange(models.Model):
null=True null=True
) )
objects = RestrictedQuerySet.as_manager() objects = ObjectChangeQuerySet.as_manager()
class Meta: class Meta:
ordering = ['-time'] ordering = ['-time']

View File

@ -1,3 +1,5 @@
from django.apps import apps
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.aggregates import JSONBAgg from django.contrib.postgres.aggregates import JSONBAgg
from django.db.models import OuterRef, Subquery, Q from django.db.models import OuterRef, Subquery, Q
@ -151,3 +153,14 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
) )
return base_query return base_query
class ObjectChangeQuerySet(RestrictedQuerySet):
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.
content_type_ids = set(
ct.pk for ct in ContentType.objects.get_for_models(*apps.get_models()).values()
)
return self.filter(changed_object_type_id__in=content_type_ids)

View File

@ -8,7 +8,6 @@ from rest_framework import status
from core.choices import ManagedFileRootPathChoices from core.choices import ManagedFileRootPathChoices
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
from extras.api.views import ReportViewSet, ScriptViewSet
from extras.models import * from extras.models import *
from extras.reports import Report from extras.reports import Report
from extras.scripts import BooleanVar, IntegerVar, Script, StringVar from extras.scripts import BooleanVar, IntegerVar, Script, StringVar
@ -579,6 +578,7 @@ class ReportTest(APITestCase):
super().setUp() super().setUp()
# Monkey-patch the API viewset's _get_report() method to return our test Report above # Monkey-patch the API viewset's _get_report() method to return our test Report above
from extras.api.views import ReportViewSet
ReportViewSet._get_report = self.get_test_report ReportViewSet._get_report = self.get_test_report
def test_get_report(self): def test_get_report(self):
@ -621,6 +621,7 @@ class ScriptTest(APITestCase):
super().setUp() super().setUp()
# Monkey-patch the API viewset's _get_script() method to return our test Script above # Monkey-patch the API viewset's _get_script() method to return our test Script above
from extras.api.views import ScriptViewSet
ScriptViewSet._get_script = self.get_test_script ScriptViewSet._get_script = self.get_test_script
def test_get_script(self): def test_get_script(self):

View File

@ -511,7 +511,7 @@ class ConfigTemplateBulkSyncDataView(generic.BulkSyncDataView):
# #
class ObjectChangeListView(generic.ObjectListView): class ObjectChangeListView(generic.ObjectListView):
queryset = ObjectChange.objects.all() queryset = ObjectChange.objects.valid_models()
filterset = filtersets.ObjectChangeFilterSet filterset = filtersets.ObjectChangeFilterSet
filterset_form = forms.ObjectChangeFilterForm filterset_form = forms.ObjectChangeFilterForm
table = tables.ObjectChangeTable table = tables.ObjectChangeTable
@ -521,10 +521,10 @@ class ObjectChangeListView(generic.ObjectListView):
@register_model_view(ObjectChange) @register_model_view(ObjectChange)
class ObjectChangeView(generic.ObjectView): class ObjectChangeView(generic.ObjectView):
queryset = ObjectChange.objects.all() queryset = ObjectChange.objects.valid_models()
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
related_changes = ObjectChange.objects.restrict(request.user, 'view').filter( related_changes = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(
request_id=instance.request_id request_id=instance.request_id
).exclude( ).exclude(
pk=instance.pk pk=instance.pk
@ -534,7 +534,7 @@ class ObjectChangeView(generic.ObjectView):
orderable=False orderable=False
) )
objectchanges = ObjectChange.objects.restrict(request.user, 'view').filter( objectchanges = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(
changed_object_type=instance.changed_object_type, changed_object_type=instance.changed_object_type,
changed_object_id=instance.changed_object_id, changed_object_id=instance.changed_object_id,
) )

View File

@ -159,7 +159,9 @@ class ProfileView(LoginRequiredMixin, View):
def get(self, request): def get(self, request):
# Compile changelog table # Compile changelog table
changelog = ObjectChange.objects.restrict(request.user, 'view').filter(user=request.user).prefetch_related( changelog = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(
user=request.user
).prefetch_related(
'changed_object_type' 'changed_object_type'
)[:20] )[:20]
changelog_table = ObjectChangeTable(changelog) changelog_table = ObjectChangeTable(changelog)