Kaynağa Gözat

Drop add_prefetch from BaseTable; improve dynamic prefetching for table querysets

Jeremy Stretch 5 yıl önce
ebeveyn
işleme
97c8306eeb

+ 0 - 6
netbox/dcim/tables/__init__.py

@@ -40,8 +40,6 @@ class ConsoleConnectionTable(BaseTable):
         verbose_name='Reachable'
     )
 
-    add_prefetch = False
-
     class Meta(BaseTable.Meta):
         model = ConsolePort
         fields = ('device', 'name', 'console_server', 'console_server_port', 'reachable')
@@ -72,8 +70,6 @@ class PowerConnectionTable(BaseTable):
         verbose_name='Reachable'
     )
 
-    add_prefetch = False
-
     class Meta(BaseTable.Meta):
         model = PowerPort
         fields = ('device', 'name', 'pdu', 'outlet', 'reachable')
@@ -107,8 +103,6 @@ class InterfaceConnectionTable(BaseTable):
         verbose_name='Reachable'
     )
 
-    add_prefetch = False
-
     class Meta(BaseTable.Meta):
         model = Interface
         fields = ('device_a', 'interface_a', 'device_b', 'interface_b', 'reachable')

+ 0 - 2
netbox/ipam/tables.py

@@ -280,8 +280,6 @@ class PrefixTable(BaseTable):
         verbose_name='Pool'
     )
 
-    add_prefetch = False
-
     class Meta(BaseTable.Meta):
         model = Prefix
         fields = (

+ 19 - 14
netbox/utilities/tables.py

@@ -14,11 +14,6 @@ class BaseTable(tables.Table):
 
     :param user: Personalize table display for the given user (optional). Has no effect if AnonymousUser is passed.
     """
-    # By default, modify the queryset passed to the table upon initialization to automatically prefetch related
-    # data. Set this to False if it's necessary to avoid modifying the queryset (e.g. to accommodate
-    # PrefixQuerySet.annotate_depth()).
-    add_prefetch = True
-
     class Meta:
         attrs = {
             'class': 'table table-hover table-headings',
@@ -61,18 +56,28 @@ class BaseTable(tables.Table):
                     self.sequence.append('actions')
 
         # Dynamically update the table's QuerySet to ensure related fields are pre-fetched
-        if self.add_prefetch and isinstance(self.data, TableQuerysetData):
-            model = getattr(self.Meta, 'model')
+        if isinstance(self.data, TableQuerysetData):
             prefetch_fields = []
             for column in self.columns:
                 if column.visible:
-                    field_path = column.accessor.split('.')
-                    try:
-                        model_field = model._meta.get_field(field_path[0])
-                        if isinstance(model_field, (RelatedField, GenericForeignKey)):
-                            prefetch_fields.append('__'.join(field_path))
-                    except FieldDoesNotExist:
-                        pass
+                    model = getattr(self.Meta, 'model')
+                    accessor = column.accessor
+                    prefetch_path = []
+                    for field_name in accessor.split(accessor.SEPARATOR):
+                        try:
+                            field = model._meta.get_field(field_name)
+                        except FieldDoesNotExist:
+                            break
+                        if isinstance(field, RelatedField):
+                            # Follow ForeignKeys to the related model
+                            prefetch_path.append(field_name)
+                            model = field.remote_field.model
+                        elif isinstance(field, GenericForeignKey):
+                            # Can't prefetch beyond a GenericForeignKey
+                            prefetch_path.append(field_name)
+                            break
+                    if prefetch_path:
+                        prefetch_fields.append('__'.join(prefetch_path))
             self.data.data = self.data.data.prefetch_related(None).prefetch_related(*prefetch_fields)
 
     @property