reindex.py 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. from django.contrib.contenttypes.models import ContentType
  2. from django.core.management.base import BaseCommand, CommandError
  3. from netbox.registry import registry
  4. from netbox.search.backends import search_backend
  5. class Command(BaseCommand):
  6. help = 'Reindex objects for search'
  7. def add_arguments(self, parser):
  8. parser.add_argument(
  9. 'args',
  10. metavar='app_label[.ModelName]',
  11. nargs='*',
  12. help='One or more apps or models to reindex',
  13. )
  14. parser.add_argument(
  15. '--lazy',
  16. action='store_true',
  17. help="For each model, reindex objects only if no cache entries already exist"
  18. )
  19. def _get_indexers(self, *model_names):
  20. indexers = {}
  21. # No models specified; pull in all registered indexers
  22. if not model_names:
  23. for idx in registry['search'].values():
  24. indexers[idx.model] = idx
  25. # Return only indexers for the specified models
  26. else:
  27. for label in model_names:
  28. labels = label.lower().split('.')
  29. # Label specifies an exact model
  30. if len(labels) == 2:
  31. app_label, model_name = labels
  32. try:
  33. idx = registry['search'][f'{app_label}.{model_name}']
  34. indexers[idx.model] = idx
  35. except KeyError:
  36. raise CommandError(f"No indexer registered for {label}")
  37. # Label specifies all the models of an app
  38. elif len(labels) == 1:
  39. app_label = labels[0] + '.'
  40. for indexer_label, idx in registry['search'].items():
  41. if indexer_label.startswith(app_label):
  42. indexers[idx.model] = idx
  43. else:
  44. raise CommandError(
  45. f"Invalid model: {label}. Model names must be in the format <app_label> or <app_label>.<model_name>."
  46. )
  47. return indexers
  48. def handle(self, *model_labels, **kwargs):
  49. # Determine which models to reindex
  50. indexers = self._get_indexers(*model_labels)
  51. if not indexers:
  52. raise CommandError("No indexers found!")
  53. self.stdout.write(f'Reindexing {len(indexers)} models.')
  54. # Clear all cached values for the specified models (if not being lazy)
  55. if not kwargs['lazy']:
  56. self.stdout.write('Clearing cached values... ', ending='')
  57. self.stdout.flush()
  58. deleted_count = search_backend.clear()
  59. self.stdout.write(f'{deleted_count} entries deleted.')
  60. # Index models
  61. self.stdout.write('Indexing models')
  62. for model, idx in indexers.items():
  63. app_label = model._meta.app_label
  64. model_name = model._meta.model_name
  65. self.stdout.write(f' {app_label}.{model_name}... ', ending='')
  66. self.stdout.flush()
  67. if kwargs['lazy']:
  68. content_type = ContentType.objects.get_for_model(model)
  69. if cached_count := search_backend.count(object_types=[content_type]):
  70. self.stdout.write(f'Skipping (found {cached_count} existing).')
  71. continue
  72. i = search_backend.cache(model.objects.iterator(), remove_existing=False)
  73. if i:
  74. self.stdout.write(f'{i} entries cached.')
  75. else:
  76. self.stdout.write(f'No objects found.')
  77. msg = f'Completed.'
  78. if total_count := search_backend.size:
  79. msg += f' Total entries: {total_count}'
  80. self.stdout.write(msg, self.style.SUCCESS)