We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Initial NetBox 3.4.x migration fails when a plugin using SearchIndex is installed
### NetBox version

3.4.1

### Python version

3.8

### Steps to Reproduce

1. Install NetBox 3.4.1
2. Install any plugin that uses SearchIndex functionality (e.g. netbox-dns) and configure it in PLUGINS
3. Run the initial migration

### Expected Behavior

The migration succeeds

### Observed Behavior

The migration fails with ProgrammingError exception:
```
Operations to perform:
  Apply all migrations: admin, auth, circuits, contenttypes, dcim, django_rq, extras, ipam, netbox_dns, sessions, social_django, taggit, tenancy, users, virtualization, wireless
Running migrations:
  Applying extras.0083_search...Reindexing 67 models.
Clearing cached values... 0 entries deleted.
Indexing models
  netbox_dns.nameserver... Traceback (most recent call last):
  File "/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedTable: relation "netbox_dns_nameserver" does not exist
LINE 1: ...name", "netbox_dns_nameserver"."description" FROM "netbox_dn...
                                                             ^


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/netbox/netbox/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/opt/netbox/lib/python3.8/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "/opt/netbox/lib/python3.8/site-packages/django/core/management/__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/opt/netbox/lib/python3.8/site-packages/django/core/management/base.py", line 402, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/opt/netbox/lib/python3.8/site-packages/django/core/management/base.py", line 448, in execute
    output = self.handle(*args, **options)
  File "/opt/netbox/lib/python3.8/site-packages/django/core/management/base.py", line 96, in wrapped
    res = handle_func(*args, **kwargs)
  File "/opt/netbox/lib/python3.8/site-packages/django/core/management/commands/migrate.py", line 349, in handle
    post_migrate_state = executor.migrate(
  File "/opt/netbox/lib/python3.8/site-packages/django/db/migrations/executor.py", line 135, in migrate
    state = self._migrate_all_forwards(
  File "/opt/netbox/lib/python3.8/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards
    state = self.apply_migration(
  File "/opt/netbox/lib/python3.8/site-packages/django/db/migrations/executor.py", line 252, in apply_migration
    state = migration.apply(state, schema_editor)
  File "/opt/netbox/lib/python3.8/site-packages/django/db/migrations/migration.py", line 130, in apply
    operation.database_forwards(
  File "/opt/netbox/lib/python3.8/site-packages/django/db/migrations/operations/special.py", line 193, in database_forwards
    self.code(from_state.apps, schema_editor)
  File "/opt/netbox/netbox/extras/migrations/0083_search.py", line 13, in reindex
    management.call_command('reindex')
  File "/opt/netbox/lib/python3.8/site-packages/django/core/management/__init__.py", line 198, in call_command
    return command.execute(*args, **defaults)
  File "/opt/netbox/lib/python3.8/site-packages/django/core/management/base.py", line 448, in execute
    output = self.handle(*args, **options)
  File "/opt/netbox/netbox/extras/management/commands/reindex.py", line 68, in handle
    i = search_backend.cache(model.objects.iterator(), remove_existing=False)
  File "/opt/netbox/netbox/netbox/search/backends.py", line 148, in cache
    for instance in instances:
  File "/opt/netbox/lib/python3.8/site-packages/django/db/models/query.py", line 512, in _iterator
    yield from iterable
  File "/opt/netbox/lib/python3.8/site-packages/django/db/models/query.py", line 87, in __iter__
    results = compiler.execute_sql(
  File "/opt/netbox/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1398, in execute_sql
    cursor.execute(sql, params)
  File "/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py", line 103, in execute
    return super().execute(sql, params)
  File "/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(
  File "/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
  File "/opt/netbox/lib/python3.8/site-packages/django/db/utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: relation "netbox_dns_nameserver" does not exist
LINE 1: ...name", "netbox_dns_nameserver"."description" FROM "netbox_dn...
```

The `netbox_dns_nameserver` relation for the `netbox_dns.models.NameServer` model uses `SearchIndex`:
```
@register_search
class NameServerIndex(SearchIndex):
    model = NameServer
    fields = (
        ("name", 100),
        ("description", 500),
    )
```

That results in the NetBox migration `extras.0083_search` trying to reindex the plugin's relation, which does not exist yet at that point.

The obvious workaround is to disable the plugin, run the migration, re-enable the plugin and then run the migration again. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `netbox/extras/management/commands/reindex.py`
Content:
```
1 from django.contrib.contenttypes.models import ContentType
2 from django.core.management.base import BaseCommand, CommandError
3 
4 from netbox.registry import registry
5 from netbox.search.backends import search_backend
6 
7 
8 class Command(BaseCommand):
9     help = 'Reindex objects for search'
10 
11     def add_arguments(self, parser):
12         parser.add_argument(
13             'args',
14             metavar='app_label[.ModelName]',
15             nargs='*',
16             help='One or more apps or models to reindex',
17         )
18 
19     def _get_indexers(self, *model_names):
20         indexers = {}
21 
22         # No models specified; pull in all registered indexers
23         if not model_names:
24             for idx in registry['search'].values():
25                 indexers[idx.model] = idx
26 
27         # Return only indexers for the specified models
28         else:
29             for label in model_names:
30                 try:
31                     app_label, model_name = label.lower().split('.')
32                 except ValueError:
33                     raise CommandError(
34                         f"Invalid model: {label}. Model names must be in the format <app_label>.<model_name>."
35                     )
36                 try:
37                     idx = registry['search'][f'{app_label}.{model_name}']
38                     indexers[idx.model] = idx
39                 except KeyError:
40                     raise CommandError(f"No indexer registered for {label}")
41 
42         return indexers
43 
44     def handle(self, *model_labels, **kwargs):
45 
46         # Determine which models to reindex
47         indexers = self._get_indexers(*model_labels)
48         if not indexers:
49             raise CommandError("No indexers found!")
50         self.stdout.write(f'Reindexing {len(indexers)} models.')
51 
52         # Clear all cached values for the specified models
53         self.stdout.write('Clearing cached values... ', ending='')
54         self.stdout.flush()
55         content_types = [
56             ContentType.objects.get_for_model(model) for model in indexers.keys()
57         ]
58         deleted_count = search_backend.clear(content_types)
59         self.stdout.write(f'{deleted_count} entries deleted.')
60 
61         # Index models
62         self.stdout.write('Indexing models')
63         for model, idx in indexers.items():
64             app_label = model._meta.app_label
65             model_name = model._meta.model_name
66             self.stdout.write(f'  {app_label}.{model_name}... ', ending='')
67             self.stdout.flush()
68             i = search_backend.cache(model.objects.iterator(), remove_existing=False)
69             if i:
70                 self.stdout.write(f'{i} entries cached.')
71             else:
72                 self.stdout.write(f'None found.')
73 
74         msg = f'Completed.'
75         if total_count := search_backend.size:
76             msg += f' Total entries: {total_count}'
77         self.stdout.write(msg, self.style.SUCCESS)
78
```

Path: `netbox/extras/migrations/0083_search.py`
Content:
```
1 import sys
2 import uuid
3 
4 import django.db.models.deletion
5 import django.db.models.lookups
6 from django.core import management
7 from django.db import migrations, models
8 
9 
10 def reindex(apps, schema_editor):
11     # Build the search index (except during tests)
12     if 'test' not in sys.argv:
13         management.call_command('reindex')
14 
15 
16 class Migration(migrations.Migration):
17 
18     dependencies = [
19         ('circuits', '0041_standardize_description_comments'),
20         ('contenttypes', '0002_remove_content_type_name'),
21         ('dcim', '0166_virtualdevicecontext'),
22         ('extras', '0082_savedfilter'),
23         ('ipam', '0063_standardize_description_comments'),
24         ('tenancy', '0009_standardize_description_comments'),
25         ('virtualization', '0034_standardize_description_comments'),
26         ('wireless', '0008_wirelesslan_status'),
27     ]
28 
29     operations = [
30         migrations.AddField(
31             model_name='customfield',
32             name='search_weight',
33             field=models.PositiveSmallIntegerField(default=1000),
34         ),
35         migrations.CreateModel(
36             name='CachedValue',
37             fields=[
38                 ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
39                 ('timestamp', models.DateTimeField(auto_now_add=True)),
40                 ('object_id', models.PositiveBigIntegerField()),
41                 ('field', models.CharField(max_length=200)),
42                 ('type', models.CharField(max_length=30)),
43                 ('value', models.TextField()),
44                 ('weight', models.PositiveSmallIntegerField(default=1000)),
45                 ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')),
46             ],
47             options={
48                 'ordering': ('weight', 'object_type', 'object_id'),
49             },
50         ),
51         migrations.RunPython(
52             code=reindex,
53             reverse_code=migrations.RunPython.noop
54         ),
55     ]
56
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/netbox/extras/management/commands/reindex.py b/netbox/extras/management/commands/reindex.py
--- a/netbox/extras/management/commands/reindex.py
+++ b/netbox/extras/management/commands/reindex.py
@@ -27,17 +27,28 @@
         # Return only indexers for the specified models
         else:
             for label in model_names:
-                try:
-                    app_label, model_name = label.lower().split('.')
-                except ValueError:
+                labels = label.lower().split('.')
+
+                # Label specifies an exact model
+                if len(labels) == 2:
+                    app_label, model_name = labels
+                    try:
+                        idx = registry['search'][f'{app_label}.{model_name}']
+                        indexers[idx.model] = idx
+                    except KeyError:
+                        raise CommandError(f"No indexer registered for {label}")
+
+                # Label specifies all the models of an app
+                elif len(labels) == 1:
+                    app_label = labels[0] + '.'
+                    for indexer_label, idx in registry['search'].items():
+                        if indexer_label.startswith(app_label):
+                            indexers[idx.model] = idx
+
+                else:
                     raise CommandError(
-                        f"Invalid model: {label}. Model names must be in the format <app_label>.<model_name>."
+                        f"Invalid model: {label}. Model names must be in the format <app_label> or <app_label>.<model_name>."
                     )
-                try:
-                    idx = registry['search'][f'{app_label}.{model_name}']
-                    indexers[idx.model] = idx
-                except KeyError:
-                    raise CommandError(f"No indexer registered for {label}")
 
         return indexers
 
diff --git a/netbox/extras/migrations/0083_search.py b/netbox/extras/migrations/0083_search.py
--- a/netbox/extras/migrations/0083_search.py
+++ b/netbox/extras/migrations/0083_search.py
@@ -10,7 +10,16 @@
 def reindex(apps, schema_editor):
     # Build the search index (except during tests)
     if 'test' not in sys.argv:
-        management.call_command('reindex')
+        management.call_command(
+            'reindex',
+            'circuits',
+            'dcim',
+            'extras',
+            'ipam',
+            'tenancy',
+            'virtualization',
+            'wireless',
+        )
 
 
 class Migration(migrations.Migration):

{"golden_diff": "diff --git a/netbox/extras/management/commands/reindex.py b/netbox/extras/management/commands/reindex.py\n--- a/netbox/extras/management/commands/reindex.py\n+++ b/netbox/extras/management/commands/reindex.py\n@@ -27,17 +27,28 @@\n         # Return only indexers for the specified models\n         else:\n             for label in model_names:\n-                try:\n-                    app_label, model_name = label.lower().split('.')\n-                except ValueError:\n+                labels = label.lower().split('.')\n+\n+                # Label specifies an exact model\n+                if len(labels) == 2:\n+                    app_label, model_name = labels\n+                    try:\n+                        idx = registry['search'][f'{app_label}.{model_name}']\n+                        indexers[idx.model] = idx\n+                    except KeyError:\n+                        raise CommandError(f\"No indexer registered for {label}\")\n+\n+                # Label specifies all the models of an app\n+                elif len(labels) == 1:\n+                    app_label = labels[0] + '.'\n+                    for indexer_label, idx in registry['search'].items():\n+                        if indexer_label.startswith(app_label):\n+                            indexers[idx.model] = idx\n+\n+                else:\n                     raise CommandError(\n-                        f\"Invalid model: {label}. Model names must be in the format <app_label>.<model_name>.\"\n+                        f\"Invalid model: {label}. Model names must be in the format <app_label> or <app_label>.<model_name>.\"\n                     )\n-                try:\n-                    idx = registry['search'][f'{app_label}.{model_name}']\n-                    indexers[idx.model] = idx\n-                except KeyError:\n-                    raise CommandError(f\"No indexer registered for {label}\")\n \n         return indexers\n \ndiff --git a/netbox/extras/migrations/0083_search.py b/netbox/extras/migrations/0083_search.py\n--- a/netbox/extras/migrations/0083_search.py\n+++ b/netbox/extras/migrations/0083_search.py\n@@ -10,7 +10,16 @@\n def reindex(apps, schema_editor):\n     # Build the search index (except during tests)\n     if 'test' not in sys.argv:\n-        management.call_command('reindex')\n+        management.call_command(\n+            'reindex',\n+            'circuits',\n+            'dcim',\n+            'extras',\n+            'ipam',\n+            'tenancy',\n+            'virtualization',\n+            'wireless',\n+        )\n \n \n class Migration(migrations.Migration):\n", "issue": "Initial NetBox 3.4.x migration fails when a plugin using SearchIndex is installed\n### NetBox version\n\n3.4.1\n\n### Python version\n\n3.8\n\n### Steps to Reproduce\n\n1. Install NetBox 3.4.1\r\n2. Install any plugin that uses SearchIndex functionality (e.g. netbox-dns) and configure it in PLUGINS\r\n3. Run the initial migration\n\n### Expected Behavior\n\nThe migration succeeds\n\n### Observed Behavior\n\nThe migration fails with ProgrammingError exception:\r\n```\r\nOperations to perform:\r\n  Apply all migrations: admin, auth, circuits, contenttypes, dcim, django_rq, extras, ipam, netbox_dns, sessions, social_django, taggit, tenancy, users, virtualization, wireless\r\nRunning migrations:\r\n  Applying extras.0083_search...Reindexing 67 models.\r\nClearing cached values... 0 entries deleted.\r\nIndexing models\r\n  netbox_dns.nameserver... Traceback (most recent call last):\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py\", line 89, in _execute\r\n    return self.cursor.execute(sql, params)\r\npsycopg2.errors.UndefinedTable: relation \"netbox_dns_nameserver\" does not exist\r\nLINE 1: ...name\", \"netbox_dns_nameserver\".\"description\" FROM \"netbox_dn...\r\n                                                             ^\r\n\r\n\r\nThe above exception was the direct cause of the following exception:\r\n\r\nTraceback (most recent call last):\r\n  File \"/opt/netbox/netbox/manage.py\", line 10, in <module>\r\n    execute_from_command_line(sys.argv)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/core/management/__init__.py\", line 446, in execute_from_command_line\r\n    utility.execute()\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/core/management/__init__.py\", line 440, in execute\r\n    self.fetch_command(subcommand).run_from_argv(self.argv)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/core/management/base.py\", line 402, in run_from_argv\r\n    self.execute(*args, **cmd_options)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/core/management/base.py\", line 448, in execute\r\n    output = self.handle(*args, **options)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/core/management/base.py\", line 96, in wrapped\r\n    res = handle_func(*args, **kwargs)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/core/management/commands/migrate.py\", line 349, in handle\r\n    post_migrate_state = executor.migrate(\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/migrations/executor.py\", line 135, in migrate\r\n    state = self._migrate_all_forwards(\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/migrations/executor.py\", line 167, in _migrate_all_forwards\r\n    state = self.apply_migration(\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/migrations/executor.py\", line 252, in apply_migration\r\n    state = migration.apply(state, schema_editor)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/migrations/migration.py\", line 130, in apply\r\n    operation.database_forwards(\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/migrations/operations/special.py\", line 193, in database_forwards\r\n    self.code(from_state.apps, schema_editor)\r\n  File \"/opt/netbox/netbox/extras/migrations/0083_search.py\", line 13, in reindex\r\n    management.call_command('reindex')\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/core/management/__init__.py\", line 198, in call_command\r\n    return command.execute(*args, **defaults)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/core/management/base.py\", line 448, in execute\r\n    output = self.handle(*args, **options)\r\n  File \"/opt/netbox/netbox/extras/management/commands/reindex.py\", line 68, in handle\r\n    i = search_backend.cache(model.objects.iterator(), remove_existing=False)\r\n  File \"/opt/netbox/netbox/netbox/search/backends.py\", line 148, in cache\r\n    for instance in instances:\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/models/query.py\", line 512, in _iterator\r\n    yield from iterable\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/models/query.py\", line 87, in __iter__\r\n    results = compiler.execute_sql(\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/models/sql/compiler.py\", line 1398, in execute_sql\r\n    cursor.execute(sql, params)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py\", line 103, in execute\r\n    return super().execute(sql, params)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py\", line 67, in execute\r\n    return self._execute_with_wrappers(\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py\", line 80, in _execute_with_wrappers\r\n    return executor(sql, params, many, context)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py\", line 89, in _execute\r\n    return self.cursor.execute(sql, params)\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/utils.py\", line 91, in __exit__\r\n    raise dj_exc_value.with_traceback(traceback) from exc_value\r\n  File \"/opt/netbox/lib/python3.8/site-packages/django/db/backends/utils.py\", line 89, in _execute\r\n    return self.cursor.execute(sql, params)\r\ndjango.db.utils.ProgrammingError: relation \"netbox_dns_nameserver\" does not exist\r\nLINE 1: ...name\", \"netbox_dns_nameserver\".\"description\" FROM \"netbox_dn...\r\n```\r\n\r\nThe `netbox_dns_nameserver` relation for the `netbox_dns.models.NameServer` model uses `SearchIndex`:\r\n```\r\n@register_search\r\nclass NameServerIndex(SearchIndex):\r\n    model = NameServer\r\n    fields = (\r\n        (\"name\", 100),\r\n        (\"description\", 500),\r\n    )\r\n```\r\n\r\nThat results in the NetBox migration `extras.0083_search` trying to reindex the plugin's relation, which does not exist yet at that point.\r\n\r\nThe obvious workaround is to disable the plugin, run the migration, re-enable the plugin and then run the migration again. \n", "before_files": [{"content": "from django.contrib.contenttypes.models import ContentType\nfrom django.core.management.base import BaseCommand, CommandError\n\nfrom netbox.registry import registry\nfrom netbox.search.backends import search_backend\n\n\nclass Command(BaseCommand):\n    help = 'Reindex objects for search'\n\n    def add_arguments(self, parser):\n        parser.add_argument(\n            'args',\n            metavar='app_label[.ModelName]',\n            nargs='*',\n            help='One or more apps or models to reindex',\n        )\n\n    def _get_indexers(self, *model_names):\n        indexers = {}\n\n        # No models specified; pull in all registered indexers\n        if not model_names:\n            for idx in registry['search'].values():\n                indexers[idx.model] = idx\n\n        # Return only indexers for the specified models\n        else:\n            for label in model_names:\n                try:\n                    app_label, model_name = label.lower().split('.')\n                except ValueError:\n                    raise CommandError(\n                        f\"Invalid model: {label}. Model names must be in the format <app_label>.<model_name>.\"\n                    )\n                try:\n                    idx = registry['search'][f'{app_label}.{model_name}']\n                    indexers[idx.model] = idx\n                except KeyError:\n                    raise CommandError(f\"No indexer registered for {label}\")\n\n        return indexers\n\n    def handle(self, *model_labels, **kwargs):\n\n        # Determine which models to reindex\n        indexers = self._get_indexers(*model_labels)\n        if not indexers:\n            raise CommandError(\"No indexers found!\")\n        self.stdout.write(f'Reindexing {len(indexers)} models.')\n\n        # Clear all cached values for the specified models\n        self.stdout.write('Clearing cached values... ', ending='')\n        self.stdout.flush()\n        content_types = [\n            ContentType.objects.get_for_model(model) for model in indexers.keys()\n        ]\n        deleted_count = search_backend.clear(content_types)\n        self.stdout.write(f'{deleted_count} entries deleted.')\n\n        # Index models\n        self.stdout.write('Indexing models')\n        for model, idx in indexers.items():\n            app_label = model._meta.app_label\n            model_name = model._meta.model_name\n            self.stdout.write(f'  {app_label}.{model_name}... ', ending='')\n            self.stdout.flush()\n            i = search_backend.cache(model.objects.iterator(), remove_existing=False)\n            if i:\n                self.stdout.write(f'{i} entries cached.')\n            else:\n                self.stdout.write(f'None found.')\n\n        msg = f'Completed.'\n        if total_count := search_backend.size:\n            msg += f' Total entries: {total_count}'\n        self.stdout.write(msg, self.style.SUCCESS)\n", "path": "netbox/extras/management/commands/reindex.py"}, {"content": "import sys\nimport uuid\n\nimport django.db.models.deletion\nimport django.db.models.lookups\nfrom django.core import management\nfrom django.db import migrations, models\n\n\ndef reindex(apps, schema_editor):\n    # Build the search index (except during tests)\n    if 'test' not in sys.argv:\n        management.call_command('reindex')\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('circuits', '0041_standardize_description_comments'),\n        ('contenttypes', '0002_remove_content_type_name'),\n        ('dcim', '0166_virtualdevicecontext'),\n        ('extras', '0082_savedfilter'),\n        ('ipam', '0063_standardize_description_comments'),\n        ('tenancy', '0009_standardize_description_comments'),\n        ('virtualization', '0034_standardize_description_comments'),\n        ('wireless', '0008_wirelesslan_status'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='customfield',\n            name='search_weight',\n            field=models.PositiveSmallIntegerField(default=1000),\n        ),\n        migrations.CreateModel(\n            name='CachedValue',\n            fields=[\n                ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),\n                ('timestamp', models.DateTimeField(auto_now_add=True)),\n                ('object_id', models.PositiveBigIntegerField()),\n                ('field', models.CharField(max_length=200)),\n                ('type', models.CharField(max_length=30)),\n                ('value', models.TextField()),\n                ('weight', models.PositiveSmallIntegerField(default=1000)),\n                ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')),\n            ],\n            options={\n                'ordering': ('weight', 'object_type', 'object_id'),\n            },\n        ),\n        migrations.RunPython(\n            code=reindex,\n            reverse_code=migrations.RunPython.noop\n        ),\n    ]\n", "path": "netbox/extras/migrations/0083_search.py"}], "after_files": [{"content": "from django.contrib.contenttypes.models import ContentType\nfrom django.core.management.base import BaseCommand, CommandError\n\nfrom netbox.registry import registry\nfrom netbox.search.backends import search_backend\n\n\nclass Command(BaseCommand):\n    help = 'Reindex objects for search'\n\n    def add_arguments(self, parser):\n        parser.add_argument(\n            'args',\n            metavar='app_label[.ModelName]',\n            nargs='*',\n            help='One or more apps or models to reindex',\n        )\n\n    def _get_indexers(self, *model_names):\n        indexers = {}\n\n        # No models specified; pull in all registered indexers\n        if not model_names:\n            for idx in registry['search'].values():\n                indexers[idx.model] = idx\n\n        # Return only indexers for the specified models\n        else:\n            for label in model_names:\n                labels = label.lower().split('.')\n\n                # Label specifies an exact model\n                if len(labels) == 2:\n                    app_label, model_name = labels\n                    try:\n                        idx = registry['search'][f'{app_label}.{model_name}']\n                        indexers[idx.model] = idx\n                    except KeyError:\n                        raise CommandError(f\"No indexer registered for {label}\")\n\n                # Label specifies all the models of an app\n                elif len(labels) == 1:\n                    app_label = labels[0] + '.'\n                    for indexer_label, idx in registry['search'].items():\n                        if indexer_label.startswith(app_label):\n                            indexers[idx.model] = idx\n\n                else:\n                    raise CommandError(\n                        f\"Invalid model: {label}. Model names must be in the format <app_label> or <app_label>.<model_name>.\"\n                    )\n\n        return indexers\n\n    def handle(self, *model_labels, **kwargs):\n\n        # Determine which models to reindex\n        indexers = self._get_indexers(*model_labels)\n        if not indexers:\n            raise CommandError(\"No indexers found!\")\n        self.stdout.write(f'Reindexing {len(indexers)} models.')\n\n        # Clear all cached values for the specified models\n        self.stdout.write('Clearing cached values... ', ending='')\n        self.stdout.flush()\n        content_types = [\n            ContentType.objects.get_for_model(model) for model in indexers.keys()\n        ]\n        deleted_count = search_backend.clear(content_types)\n        self.stdout.write(f'{deleted_count} entries deleted.')\n\n        # Index models\n        self.stdout.write('Indexing models')\n        for model, idx in indexers.items():\n            app_label = model._meta.app_label\n            model_name = model._meta.model_name\n            self.stdout.write(f'  {app_label}.{model_name}... ', ending='')\n            self.stdout.flush()\n            i = search_backend.cache(model.objects.iterator(), remove_existing=False)\n            if i:\n                self.stdout.write(f'{i} entries cached.')\n            else:\n                self.stdout.write(f'None found.')\n\n        msg = f'Completed.'\n        if total_count := search_backend.size:\n            msg += f' Total entries: {total_count}'\n        self.stdout.write(msg, self.style.SUCCESS)\n", "path": "netbox/extras/management/commands/reindex.py"}, {"content": "import sys\nimport uuid\n\nimport django.db.models.deletion\nimport django.db.models.lookups\nfrom django.core import management\nfrom django.db import migrations, models\n\n\ndef reindex(apps, schema_editor):\n    # Build the search index (except during tests)\n    if 'test' not in sys.argv:\n        management.call_command(\n            'reindex',\n            'circuits',\n            'dcim',\n            'extras',\n            'ipam',\n            'tenancy',\n            'virtualization',\n            'wireless',\n        )\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('circuits', '0041_standardize_description_comments'),\n        ('contenttypes', '0002_remove_content_type_name'),\n        ('dcim', '0166_virtualdevicecontext'),\n        ('extras', '0082_savedfilter'),\n        ('ipam', '0063_standardize_description_comments'),\n        ('tenancy', '0009_standardize_description_comments'),\n        ('virtualization', '0034_standardize_description_comments'),\n        ('wireless', '0008_wirelesslan_status'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='customfield',\n            name='search_weight',\n            field=models.PositiveSmallIntegerField(default=1000),\n        ),\n        migrations.CreateModel(\n            name='CachedValue',\n            fields=[\n                ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),\n                ('timestamp', models.DateTimeField(auto_now_add=True)),\n                ('object_id', models.PositiveBigIntegerField()),\n                ('field', models.CharField(max_length=200)),\n                ('type', models.CharField(max_length=30)),\n                ('value', models.TextField()),\n                ('weight', models.PositiveSmallIntegerField(default=1000)),\n                ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')),\n            ],\n            options={\n                'ordering': ('weight', 'object_type', 'object_id'),\n            },\n        ),\n        migrations.RunPython(\n            code=reindex,\n            reverse_code=migrations.RunPython.noop\n        ),\n    ]\n", "path": "netbox/extras/migrations/0083_search.py"}]}