瀏覽代碼

Define PK allocator functions in utilities.pk_allocation and re-export from netbox.models

Jeremy Stretch 3 天之前
父節點
當前提交
88b87a833c
共有 2 個文件被更改,包括 55 次插入 和 64 次删除
  1. 2 57
      netbox/netbox/models/__init__.py
  2. 53 7
      netbox/utilities/pk_allocation.py

+ 2 - 57
netbox/netbox/models/__init__.py

@@ -2,8 +2,7 @@ from django.conf import settings
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.validators import ValidationError
 from django.core.validators import ValidationError
-from django.db import models, router
-from django.db.models.fields import AutoFieldMixin
+from django.db import models
 from django.urls import reverse
 from django.urls import reverse
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 from mptt.models import MPTTModel, TreeForeignKey
 from mptt.models import MPTTModel, TreeForeignKey
@@ -11,8 +10,8 @@ from mptt.models import MPTTModel, TreeForeignKey
 from netbox.models.features import *
 from netbox.models.features import *
 from netbox.models.ltree import LtreeManager, LtreeModel, SortPathField
 from netbox.models.ltree import LtreeManager, LtreeModel, SortPathField
 from netbox.models.mixins import OwnerMixin
 from netbox.models.mixins import OwnerMixin
-from netbox.registry import registry
 from utilities.mptt import TreeManager
 from utilities.mptt import TreeManager
+from utilities.pk_allocation import allocate_pks, register_pk_allocator
 from utilities.querysets import RestrictedQuerySet
 from utilities.querysets import RestrictedQuerySet
 
 
 __all__ = (
 __all__ = (
@@ -324,57 +323,3 @@ class AdminModel(
 
 
     class Meta:
     class Meta:
         abstract = True
         abstract = True
-
-
-#
-# Primary key allocation
-#
-
-def register_pk_allocator(func):
-    """
-    Decorator for registering a primary key allocator: a callable which may supply primary keys for new objects.
-
-    The callable must accept a model class, a count, and the alias of the database to which the new objects will be
-    written (`using`), and return either a list of exactly `count` primary keys for that model, or None to defer to the
-    next allocator (and ultimately to the database). Allocators are consulted in the order in which they were
-    registered. Registering an allocator which is already registered has no effect.
-
-        @register_pk_allocator
-        def my_allocator(model, count, using):
-            ...
-    """
-    if func not in registry['pk_allocators']:
-        registry['pk_allocators'].append(func)
-
-    return func
-
-
-def allocate_pks(model, count=1, using=None):
-    """
-    Return a list of `count` primary keys for new instances of `model` from the first registered allocator which
-    supplies them, or None if the database should assign them. `using` is the alias of the database to which the new
-    instances will be written; if omitted, the database router's choice for writing `model` is assumed. Code which
-    inserts rows outside the ORM (e.g. using raw SQL or COPY) can call this to obtain primary keys in the same manner
-    as Model.save() and QuerySet.bulk_create().
-    """
-    if using is None:
-        using = router.db_for_write(model)
-    # Allocators receive the model which owns the primary key column (the concrete parent, for a proxy or multi-table
-    # inheritance child), and only if it's an auto field
-    pk = model._meta.pk
-    while pk.remote_field and pk.remote_field.parent_link:
-        pk = pk.target_field
-    if not isinstance(pk, AutoFieldMixin):
-        return None
-    model = pk.model
-    for allocator in registry['pk_allocators']:
-        if (pks := allocator(model, count, using)) is not None:
-            pks = list(pks)
-            if len(pks) != count:
-                name = getattr(allocator, '__qualname__', allocator)
-                raise ValueError(
-                    f'Primary key allocator {name} returned {len(pks)} primary keys for {model._meta.label} '
-                    f'(expected {count}).'
-                )
-            return pks
-    return None

+ 53 - 7
netbox/utilities/pk_allocation.py

@@ -1,11 +1,13 @@
-from django.db import models
+from django.db import models, router
 from django.db.models.fields import AutoFieldMixin
 from django.db.models.fields import AutoFieldMixin
 
 
 from netbox.registry import registry
 from netbox.registry import registry
 
 
 __all__ = (
 __all__ = (
+    'allocate_pks',
     'bulk_create_with_pk_allocation',
     'bulk_create_with_pk_allocation',
     'prepare_for_bulk_create_with_pk_allocation',
     'prepare_for_bulk_create_with_pk_allocation',
+    'register_pk_allocator',
     'save_table_with_pk_allocation',
     'save_table_with_pk_allocation',
 )
 )
 
 
@@ -16,6 +18,56 @@ _prepare_for_bulk_create = models.QuerySet._prepare_for_bulk_create
 _save_table = models.Model._save_table
 _save_table = models.Model._save_table
 
 
 
 
+def register_pk_allocator(func):
+    """
+    Decorator for registering a primary key allocator: a callable which may supply primary keys for new objects.
+
+    The callable must accept a model class, a count, and the alias of the database to which the new objects will be
+    written (`using`), and return either a list of exactly `count` primary keys for that model, or None to defer to the
+    next allocator (and ultimately to the database). Allocators are consulted in the order in which they were
+    registered. Registering an allocator which is already registered has no effect.
+
+        @register_pk_allocator
+        def my_allocator(model, count, using):
+            ...
+    """
+    if func not in registry['pk_allocators']:
+        registry['pk_allocators'].append(func)
+
+    return func
+
+
+def allocate_pks(model, count=1, using=None):
+    """
+    Return a list of `count` primary keys for new instances of `model` from the first registered allocator which
+    supplies them, or None if the database should assign them. `using` is the alias of the database to which the new
+    instances will be written; if omitted, the database router's choice for writing `model` is assumed. Code which
+    inserts rows outside the ORM (e.g. using raw SQL or COPY) can call this to obtain primary keys in the same manner
+    as Model.save() and QuerySet.bulk_create().
+    """
+    if using is None:
+        using = router.db_for_write(model)
+    # Allocators receive the model which owns the primary key column (the concrete parent, for a proxy or multi-table
+    # inheritance child), and only if it's an auto field
+    pk = model._meta.pk
+    while pk.remote_field and pk.remote_field.parent_link:
+        pk = pk.target_field
+    if not isinstance(pk, AutoFieldMixin):
+        return None
+    model = pk.model
+    for allocator in registry['pk_allocators']:
+        if (pks := allocator(model, count, using)) is not None:
+            pks = list(pks)
+            if len(pks) != count:
+                name = getattr(allocator, '__qualname__', allocator)
+                raise ValueError(
+                    f'Primary key allocator {name} returned {len(pks)} primary keys for {model._meta.label} '
+                    f'(expected {count}).'
+                )
+            return pks
+    return None
+
+
 def _can_allocate_pks(model):
 def _can_allocate_pks(model):
     """
     """
     Return True if primary keys for new instances of the model may be obtained from the registered allocators.
     Return True if primary keys for new instances of the model may be obtained from the registered allocators.
@@ -39,9 +91,6 @@ def save_table_with_pk_allocation(
     Allocators receive the model which owns the primary key column (the concrete parent, for a proxy or multi-table
     Allocators receive the model which owns the primary key column (the concrete parent, for a proxy or multi-table
     inheritance child), and the alias of the database being written to.
     inheritance child), and the alias of the database being written to.
     """
     """
-    # This module is imported before the app registry is ready, when netbox.models cannot yet be imported
-    from netbox.models import allocate_pks
-
     pk = cls._meta.pk
     pk = cls._meta.pk
     concrete_model = instance._meta.concrete_model
     concrete_model = instance._meta.concrete_model
     # The values to restore if the save fails, carried over from the insertion of any multi-table inheritance parents
     # The values to restore if the save fails, carried over from the insertion of any multi-table inheritance parents
@@ -86,9 +135,6 @@ def prepare_for_bulk_create_with_pk_allocation(queryset, objs):
     The prior state of each object which receives one is recorded on the queryset, for bulk_create_with_pk_allocation()
     The prior state of each object which receives one is recorded on the queryset, for bulk_create_with_pk_allocation()
     to restore.
     to restore.
     """
     """
-    # This module is imported before the app registry is ready, when netbox.models cannot yet be imported
-    from netbox.models import allocate_pks
-
     objs_with_pk, objs_without_pk = _prepare_for_bulk_create(queryset, objs)
     objs_with_pk, objs_without_pk = _prepare_for_bulk_create(queryset, objs)
     pk = queryset.model._meta.pk
     pk = queryset.model._meta.pk
     # QuerySet.db resolves to the database being written to, since bulk_create() has already set _for_write
     # QuerySet.db resolves to the database being written to, since bulk_create() has already set _for_write