|
|
@@ -437,30 +437,12 @@ class BulkImportView(GetReturnURLMixin, BaseMultiObjectView):
|
|
|
"""
|
|
|
return object_form.save()
|
|
|
|
|
|
- def create_and_update_objects(self, form, request):
|
|
|
+ def _process_import_records(self, form, request, records, prefetched_objects):
|
|
|
+ """
|
|
|
+ Process CSV import records and save objects.
|
|
|
+ """
|
|
|
saved_objects = []
|
|
|
|
|
|
- records = list(form.cleaned_data['data'])
|
|
|
-
|
|
|
- # Prefetch objects to be updated, if any
|
|
|
- prefetch_ids = [int(record['id']) for record in records if record.get('id')]
|
|
|
-
|
|
|
- # check for duplicate IDs
|
|
|
- duplicate_pks = [pk for pk, count in Counter(prefetch_ids).items() if count > 1]
|
|
|
- if duplicate_pks:
|
|
|
- error_msg = _(
|
|
|
- "Duplicate objects found: {model} with ID(s) {ids} appears multiple times"
|
|
|
- ).format(
|
|
|
- model=title(self.queryset.model._meta.verbose_name),
|
|
|
- ids=', '.join(str(pk) for pk in sorted(duplicate_pks))
|
|
|
- )
|
|
|
- raise ValidationError(error_msg)
|
|
|
-
|
|
|
- prefetched_objects = {
|
|
|
- obj.pk: obj
|
|
|
- for obj in self.queryset.model.objects.filter(id__in=prefetch_ids)
|
|
|
- } if prefetch_ids else {}
|
|
|
-
|
|
|
for i, record in enumerate(records, start=1):
|
|
|
object_id = int(record.pop('id')) if record.get('id') else None
|
|
|
|
|
|
@@ -524,6 +506,37 @@ class BulkImportView(GetReturnURLMixin, BaseMultiObjectView):
|
|
|
|
|
|
return saved_objects
|
|
|
|
|
|
+ def create_and_update_objects(self, form, request):
|
|
|
+ records = list(form.cleaned_data['data'])
|
|
|
+
|
|
|
+ # Prefetch objects to be updated, if any
|
|
|
+ prefetch_ids = [int(record['id']) for record in records if record.get('id')]
|
|
|
+
|
|
|
+ # check for duplicate IDs
|
|
|
+ duplicate_pks = [pk for pk, count in Counter(prefetch_ids).items() if count > 1]
|
|
|
+ if duplicate_pks:
|
|
|
+ error_msg = _(
|
|
|
+ "Duplicate objects found: {model} with ID(s) {ids} appears multiple times"
|
|
|
+ ).format(
|
|
|
+ model=title(self.queryset.model._meta.verbose_name),
|
|
|
+ ids=', '.join(str(pk) for pk in sorted(duplicate_pks))
|
|
|
+ )
|
|
|
+ raise ValidationError(error_msg)
|
|
|
+
|
|
|
+ prefetched_objects = {
|
|
|
+ obj.pk: obj
|
|
|
+ for obj in self.queryset.model.objects.filter(id__in=prefetch_ids)
|
|
|
+ } if prefetch_ids else {}
|
|
|
+
|
|
|
+ # For MPTT models, delay tree updates until all saves are complete
|
|
|
+ if issubclass(self.queryset.model, MPTTModel):
|
|
|
+ with self.queryset.model.objects.delay_mptt_updates():
|
|
|
+ saved_objects = self._process_import_records(form, request, records, prefetched_objects)
|
|
|
+ else:
|
|
|
+ saved_objects = self._process_import_records(form, request, records, prefetched_objects)
|
|
|
+
|
|
|
+ return saved_objects
|
|
|
+
|
|
|
#
|
|
|
# Request handlers
|
|
|
#
|
|
|
@@ -893,9 +906,16 @@ class BulkRenameView(GetReturnURLMixin, BaseMultiObjectView):
|
|
|
renamed_pks = self._rename_objects(form, selected_objects)
|
|
|
|
|
|
if '_apply' in request.POST:
|
|
|
- for obj in selected_objects:
|
|
|
- setattr(obj, self.field_name, obj.new_name)
|
|
|
- obj.save()
|
|
|
+ # For MPTT models, delay tree updates until all saves are complete
|
|
|
+ if issubclass(self.queryset.model, MPTTModel):
|
|
|
+ with self.queryset.model.objects.delay_mptt_updates():
|
|
|
+ for obj in selected_objects:
|
|
|
+ setattr(obj, self.field_name, obj.new_name)
|
|
|
+ obj.save()
|
|
|
+ else:
|
|
|
+ for obj in selected_objects:
|
|
|
+ setattr(obj, self.field_name, obj.new_name)
|
|
|
+ obj.save()
|
|
|
|
|
|
# Enforce constrained permissions
|
|
|
if self.queryset.filter(pk__in=renamed_pks).count() != len(selected_objects):
|