-
Notifications
You must be signed in to change notification settings - Fork 18
Feature: add bulk_save to HistoryModel #408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,9 +6,10 @@ | |||||||||||||||||||||||
| import datetime as base_datetime | ||||||||||||||||||||||||
| from dirtyfields import DirtyFieldsMixin | ||||||||||||||||||||||||
| from django.core.exceptions import ValidationError | ||||||||||||||||||||||||
| from django.db import models | ||||||||||||||||||||||||
| from django.db import models, transaction | ||||||||||||||||||||||||
| from django.db.models import F | ||||||||||||||||||||||||
| from simple_history.models import HistoricalRecords | ||||||||||||||||||||||||
| from simple_history.utils import bulk_create_with_history, bulk_update_with_history | ||||||||||||||||||||||||
| from core.utils import CachedManager, CachedModelMixin | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # from core.datetimes.ad_datetime import datetime as py_datetime | ||||||||||||||||||||||||
|
|
@@ -217,6 +218,77 @@ def copy(self, exclude_fields=["id", "uuid"]): | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| return new_instance | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @classmethod | ||||||||||||||||||||||||
| def bulk_save(cls, data_list, user, batch_size=100): | ||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||
| Efficiently update or create multiple instances based on 'id' field. | ||||||||||||||||||||||||
| All operations are atomic - either all succeed or all fail. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||
| data_list: List of dicts with instance data (with or without 'id') | ||||||||||||||||||||||||
| user: User performing the operation | ||||||||||||||||||||||||
| batch_size: Number of records to process per batch | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||
| dict with 'created' and 'updated' counts | ||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||
| if not data_list: | ||||||||||||||||||||||||
| return {'created': 0, 'updated': 0} | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| now = py_datetime.now() | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ids_to_update = [d['id'] for d in data_list if d.get('id')] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| existing = {obj.id: obj for obj in cls.objects.filter(id__in=ids_to_update, is_deleted=False)} | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
weilu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| to_create = [] | ||||||||||||||||||||||||
| to_update = [] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| exclude_fields = {'id', 'uuid', 'date_created', 'user_created', 'date_updated', | ||||||||||||||||||||||||
| 'user_updated', 'version', 'is_deleted', 'date_valid_from', | ||||||||||||||||||||||||
| 'date_valid_to', 'replacement_uuid'} | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| for data in data_list: | ||||||||||||||||||||||||
| record_id = data.get('id') | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if record_id and record_id in existing: | ||||||||||||||||||||||||
| instance = existing[record_id] | ||||||||||||||||||||||||
| for field, value in data.items(): | ||||||||||||||||||||||||
| if field not in exclude_fields: | ||||||||||||||||||||||||
| setattr(instance, field, value) | ||||||||||||||||||||||||
| instance.user_updated = user | ||||||||||||||||||||||||
| instance.date_updated = now | ||||||||||||||||||||||||
| instance.version = instance.version + 1 | ||||||||||||||||||||||||
| to_update.append(instance) | ||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||
| create_data = {k: v for k, v in data.items() if k not in exclude_fields} | ||||||||||||||||||||||||
| instance = cls(**create_data) | ||||||||||||||||||||||||
| instance.set_pk() | ||||||||||||||||||||||||
| instance.user_created = user | ||||||||||||||||||||||||
| instance.user_updated = user | ||||||||||||||||||||||||
| instance.date_created = now | ||||||||||||||||||||||||
| instance.date_updated = now | ||||||||||||||||||||||||
| instance.version = 1 | ||||||||||||||||||||||||
| to_create.append(instance) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| with transaction.atomic(): | ||||||||||||||||||||||||
| created_count = 0 | ||||||||||||||||||||||||
| updated_count = 0 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if to_create: | ||||||||||||||||||||||||
| bulk_create_with_history(to_create, cls, batch_size=batch_size, default_user=user) | ||||||||||||||||||||||||
| created_count = len(to_create) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if to_update: | ||||||||||||||||||||||||
| update_fields = [f for f in to_update[0].__dict__.keys() | ||||||||||||||||||||||||
| if not f.startswith('_') and f not in exclude_fields] | ||||||||||||||||||||||||
|
Comment on lines
+283
to
+284
|
||||||||||||||||||||||||
| update_fields = [f for f in to_update[0].__dict__.keys() | |
| if not f.startswith('_') and f not in exclude_fields] | |
| update_fields = [ | |
| field.name for field in cls._meta.get_fields() | |
| if ( | |
| field.concrete | |
| and not field.auto_created | |
| and not (field.many_to_many or field.one_to_many) | |
| and field.name not in exclude_fields | |
| ) | |
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can it be default at None and retrieve it from the get_current_user() ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Models shouldn't be aware of current user as that's at the controller layer.