2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-05-06 09:43:38 +00:00

[refactor] bulk operation batch size (#11864)

* Fetch models properly during migration

* Specify batch_size when performing bulk_update operations

* Enforce batch_size for bulk_create operations

* Fix typo

* Revert typo fix

- Changes API schema
This commit is contained in:
Oliver
2026-05-05 00:31:37 +10:00
committed by GitHub
parent 00d6f1c3ab
commit 0c9cb5e4a4
15 changed files with 40 additions and 30 deletions
+1 -1
View File
@@ -594,7 +594,7 @@ class InvenTreeParameterMixin(InvenTreePermissionCheckMixin, models.Model):
parameters.append(parameter) parameters.append(parameter)
if len(parameters) > 0: if len(parameters) > 0:
common.models.Parameter.objects.bulk_create(parameters) common.models.Parameter.objects.bulk_create(parameters, batch_size=250)
def get_parameter(self, name: str): def get_parameter(self, name: str):
"""Return a Parameter instance for the given parameter name. """Return a Parameter instance for the given parameter name.
+5 -5
View File
@@ -975,10 +975,10 @@ class Build(
allocations.extend(new_allocations) allocations.extend(new_allocations)
# Bulk create tracking entries # Bulk create tracking entries
stock.models.StockItemTracking.objects.bulk_create(tracking) stock.models.StockItemTracking.objects.bulk_create(tracking, batch_size=250)
# Generate stock allocations # Generate stock allocations
BuildItem.objects.bulk_create(allocations) BuildItem.objects.bulk_create(allocations, batch_size=250)
else: else:
"""Create a single build output of the given quantity.""" """Create a single build output of the given quantity."""
@@ -1389,7 +1389,7 @@ class Build(
new_items.extend(self.auto_allocate_tracked_output(output, **kwargs)) new_items.extend(self.auto_allocate_tracked_output(output, **kwargs))
# Bulk-create the new BuildItem objects # Bulk-create the new BuildItem objects
BuildItem.objects.bulk_create(new_items) BuildItem.objects.bulk_create(new_items, batch_size=250)
def auto_allocate_untracked_stock(self, **kwargs): def auto_allocate_untracked_stock(self, **kwargs):
"""Automatically allocate untracked stock items against this build order. """Automatically allocate untracked stock items against this build order.
@@ -1530,7 +1530,7 @@ class Build(
break break
# Bulk-create the new BuildItem objects # Bulk-create the new BuildItem objects
BuildItem.objects.bulk_create(new_items) BuildItem.objects.bulk_create(new_items, batch_size=250)
def unallocated_lines(self, tracked: Optional[bool] = None) -> QuerySet: def unallocated_lines(self, tracked: Optional[bool] = None) -> QuerySet:
"""Returns a list of BuildLine objects which have not been fully allocated.""" """Returns a list of BuildLine objects which have not been fully allocated."""
@@ -1655,7 +1655,7 @@ class Build(
lines.append(BuildLine(build=self, bom_item=bom_item, quantity=quantity)) lines.append(BuildLine(build=self, bom_item=bom_item, quantity=quantity))
BuildLine.objects.bulk_create(lines) BuildLine.objects.bulk_create(lines, batch_size=250)
if len(lines) > 0: if len(lines) > 0:
logger.info('Created %s BuildLine objects for BuildOrder', len(lines)) logger.info('Created %s BuildLine objects for BuildOrder', len(lines))
+5 -2
View File
@@ -247,11 +247,14 @@ class BaseInvenTreeSetting(models.Model):
if len(missing_keys) > 0: if len(missing_keys) > 0:
logger.info('Building %s default values for %s', len(missing_keys), cls) logger.info('Building %s default values for %s', len(missing_keys), cls)
cls.objects.bulk_create([ cls.objects.bulk_create(
[
cls(key=key, value=cls.get_setting_default(key), **kwargs) cls(key=key, value=cls.get_setting_default(key), **kwargs)
for key in missing_keys for key in missing_keys
if not key.startswith('_') if not key.startswith('_')
]) ],
batch_size=250,
)
except Exception as exc: except Exception as exc:
logger.exception( logger.exception(
'Failed to build default values for %s (%s)', cls, type(exc) 'Failed to build default values for %s (%s)', cls, type(exc)
+2 -2
View File
@@ -241,7 +241,7 @@ class DataImportSession(models.Model):
) )
# Create the column mappings # Create the column mappings
DataImportColumnMap.objects.bulk_create(column_mappings) DataImportColumnMap.objects.bulk_create(column_mappings, batch_size=250)
self.status = DataImportStatusCode.MAPPING.value self.status = DataImportStatusCode.MAPPING.value
self.save() self.save()
@@ -337,7 +337,7 @@ class DataImportSession(models.Model):
imported_rows.append(row) imported_rows.append(row)
# Perform database writes as a single operation # Perform database writes as a single operation
DataImportRow.objects.bulk_create(imported_rows) DataImportRow.objects.bulk_create(imported_rows, batch_size=250)
# Mark the import task as "PROCESSING" # Mark the import task as "PROCESSING"
self.status = DataImportStatusCode.PROCESSING.value self.status = DataImportStatusCode.PROCESSING.value
+6 -2
View File
@@ -1173,7 +1173,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
# Bulk create new stock items # Bulk create new stock items
if len(bulk_create_items) > 0: if len(bulk_create_items) > 0:
stock.models.StockItem.objects.bulk_create(bulk_create_items) stock.models.StockItem.objects.bulk_create(
bulk_create_items, batch_size=250
)
# Fetch them back again # Fetch them back again
tree_ids = [item.tree_id for item in bulk_create_items] tree_ids = [item.tree_id for item in bulk_create_items]
@@ -1200,7 +1202,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
) )
# Bulk create new tracking entries for each item # Bulk create new tracking entries for each item
stock.models.StockItemTracking.objects.bulk_create(tracking_entries) stock.models.StockItemTracking.objects.bulk_create(
tracking_entries, batch_size=250
)
# Update received quantity for each line item # Update received quantity for each line item
PurchaseOrderLineItem.objects.bulk_update(line_items_to_update, ['received']) PurchaseOrderLineItem.objects.bulk_update(line_items_to_update, ['received'])
+3 -1
View File
@@ -1901,7 +1901,9 @@ class SalesOrderSerialAllocationSerializer(serializers.Serializer):
) )
with transaction.atomic(): with transaction.atomic():
order.models.SalesOrderAllocation.objects.bulk_create(allocations) order.models.SalesOrderAllocation.objects.bulk_create(
allocations, batch_size=250
)
class SalesOrderShipmentAllocationSerializer(serializers.Serializer): class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
@@ -24,6 +24,7 @@ def update_category_parameters(apps, schema_editor):
PartCategoryParameterTemplate.objects.bulk_update( PartCategoryParameterTemplate.objects.bulk_update(
category_parameters_to_update, category_parameters_to_update,
fields=["template"], fields=["template"],
batch_size=250,
) )
@@ -47,6 +48,7 @@ def reverse_update_category_parameters(apps, schema_editor):
PartCategoryParameterTemplate.objects.bulk_update( PartCategoryParameterTemplate.objects.bulk_update(
category_parameters_to_update, category_parameters_to_update,
fields=["parameter_template"], fields=["parameter_template"],
batch_size=250,
) )
@@ -10,9 +10,10 @@ def update_bom_role(apps, schema_editor):
we need to update the ruleset to include the correct models. we need to update the ruleset to include the correct models.
""" """
from django.contrib.auth.models import Group
from users.ruleset import RuleSetEnum from users.ruleset import RuleSetEnum
from users.models import RuleSet
Group = apps.get_model("auth", "Group")
RuleSet = apps.get_model("users", "RuleSet")
# For each existing group, create a new 'bom' ruleset # For each existing group, create a new 'bom' ruleset
for group in Group.objects.all(): for group in Group.objects.all():
+2 -2
View File
@@ -2462,7 +2462,7 @@ class Part(
templates.append(template) templates.append(template)
if len(templates) > 0: if len(templates) > 0:
PartTestTemplate.objects.bulk_create(templates) PartTestTemplate.objects.bulk_create(templates, batch_size=250)
@transaction.atomic @transaction.atomic
def copy_category_parameters(self, category: PartCategory): def copy_category_parameters(self, category: PartCategory):
@@ -2504,7 +2504,7 @@ class Part(
) )
) )
Parameter.objects.bulk_create(parameters) Parameter.objects.bulk_create(parameters, batch_size=250)
def getTestTemplates( def getTestTemplates(
self, required=None, include_parent: bool = True, enabled=None self, required=None, include_parent: bool = True, enabled=None
+1 -1
View File
@@ -208,7 +208,7 @@ def perform_stocktake(
if generate_entry: if generate_entry:
# Bulk-create PartStocktake entries # Bulk-create PartStocktake entries
part_models.PartStocktake.objects.bulk_create(history_entries) part_models.PartStocktake.objects.bulk_create(history_entries, batch_size=250)
if report_output: if report_output:
# Save report data, and mark as complete # Save report data, and mark as complete
@@ -55,7 +55,7 @@ class InvenTreeUINotifications(NotificationMixin, InvenTreePlugin):
) )
) )
NotificationMessage.objects.bulk_create(entries) NotificationMessage.objects.bulk_create(entries, batch_size=250)
return True return True
+1 -1
View File
@@ -1234,7 +1234,7 @@ class StockList(
): ):
tracking.append(entry) tracking.append(entry)
StockItemTracking.objects.bulk_create(tracking) StockItemTracking.objects.bulk_create(tracking, batch_size=250)
# Annotate the stock items with part information # Annotate the stock items with part information
queryset = StockSerializers.StockItemSerializer.annotate_queryset(items) queryset = StockSerializers.StockItemSerializer.annotate_queryset(items)
+3 -3
View File
@@ -634,7 +634,7 @@ class StockItem(
items.append(StockItem(**data)) items.append(StockItem(**data))
# Create the StockItem objects in bulk # Create the StockItem objects in bulk
StockItem.objects.bulk_create(items) StockItem.objects.bulk_create(items, batch_size=250)
# We will need to rebuild the stock item tree manually, due to the bulk_create operation # We will need to rebuild the stock item tree manually, due to the bulk_create operation
if parent and parent.tree_id: if parent and parent.tree_id:
@@ -1973,7 +1973,7 @@ class StockItem(
# Copy any test results from this item to the new one # Copy any test results from this item to the new one
item.copyTestResultsFrom(self) item.copyTestResultsFrom(self)
StockItemTracking.objects.bulk_create(history_items) StockItemTracking.objects.bulk_create(history_items, batch_size=250)
# Remove the equivalent number of items # Remove the equivalent number of items
self.take_stock( self.take_stock(
@@ -2008,7 +2008,7 @@ class StockItem(
result.stock_item = self result.stock_item = self
results_to_create.append(result) results_to_create.append(result)
StockItemTestResult.objects.bulk_create(results_to_create) StockItemTestResult.objects.bulk_create(results_to_create, batch_size=250)
def add_test_result(self, create_template=True, **kwargs): def add_test_result(self, create_template=True, **kwargs):
"""Helper function to add a new StockItemTestResult. """Helper function to add a new StockItemTestResult.
+1 -1
View File
@@ -1128,7 +1128,7 @@ class StockChangeStatusSerializer(serializers.Serializer):
) )
# Create tracking entries # Create tracking entries
StockItemTracking.objects.bulk_create(transaction_notes) StockItemTracking.objects.bulk_create(transaction_notes, batch_size=250)
class StockLocationTypeSerializer(InvenTree.serializers.InvenTreeModelSerializer): class StockLocationTypeSerializer(InvenTree.serializers.InvenTreeModelSerializer):
+1 -3
View File
@@ -602,9 +602,7 @@ class StockItemListTest(StockAPITestCase):
) )
) )
if len(items) >= 100: StockItem.objects.bulk_create(items, batch_size=250)
StockItem.objects.bulk_create(items)
items = []
self.assertEqual(StockItem.objects.count(), 1000) self.assertEqual(StockItem.objects.count(), 1000)