Skip to content

Commit ddc338a

Browse files
Added support for skip_deletes, skip_creates, and skip_updates in bulk_sync method.
1 parent bec2bab commit ddc338a

File tree

2 files changed

+64
-9
lines changed

2 files changed

+64
-9
lines changed

bulk_sync/__init__.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
from collections import OrderedDict
22
import logging
3-
43
from django.db import transaction
54

65
logger = logging.getLogger(__name__)
76

87

9-
def bulk_sync(new_models, key_fields, filters, batch_size=None, fields=None):
8+
def bulk_sync(new_models, key_fields, filters, batch_size=None, fields=None, skip_creates=False, skip_updates=False, skip_deletes=False):
109
""" Combine bulk create, update, and delete. Make the DB match a set of in-memory objects.
1110
1211
`new_models`: Django ORM objects that are the desired state. They may or may not have `id` set.
@@ -50,21 +49,25 @@ def get_key(obj):
5049
new_obj.id = old_obj.id
5150
existing_objs.append(new_obj)
5251

52+
if skip_creates is False:
5353
db_class.objects.bulk_create(new_objs, batch_size=batch_size)
54+
55+
if skip_updates is False:
5456
db_class.objects.bulk_update(existing_objs, fields=fields, batch_size=batch_size)
5557

56-
# delete stale ones...
58+
if skip_deletes is False:
59+
# delete stale objects
5760
objs.filter(pk__in=[_.pk for _ in list(obj_dict.values())]).delete()
5861

59-
assert len(existing_objs) == len(new_models) - len(new_objs)
62+
assert len(existing_objs) == len(new_models) - len(new_objs)
6063

61-
stats = {"created": len(new_objs), "updated": len(new_models) - len(new_objs), "deleted": len(obj_dict)}
64+
stats = {"created": len(new_objs), "updated": len(new_models) - len(new_objs), "deleted": len(obj_dict)}
6265

63-
logger.debug(
64-
"{}: {} created, {} updated, {} deleted.".format(
65-
db_class.__name__, stats["created"], stats["updated"], stats["deleted"]
66-
)
66+
logger.debug(
67+
"{}: {} created, {} updated, {} deleted.".format(
68+
db_class.__name__, stats["created"], stats["updated"], stats["deleted"]
6769
)
70+
)
6871

6972
return {"stats": stats}
7073

tests/tests.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,58 @@ def test_fields_parameter(self):
104104
self.assertEqual(0, ret["stats"]["created"])
105105
self.assertEqual(0, ret["stats"]["deleted"])
106106

107+
def test_skip_deletes(self):
108+
c1 = Company.objects.create(name="My Company LLC")
109+
110+
e1 = Employee.objects.create(name="Scott", age=40, company=c1)
111+
e2 = Employee.objects.create(name="Isaac", age=9, company=c1)
112+
113+
# update Scott - this makes Isaac is the "stale object" that would be deleted if skip_deletes were False
114+
new_objs = [
115+
Employee(name="Scott", age=41, company=c1),
116+
]
117+
118+
# but Isaac should remain when the skip_deletes flag is True
119+
ret = bulk_sync(new_models=new_objs, filters=None, key_fields=("name",), skip_deletes=True)
120+
121+
self.assertEqual(2, Employee.objects.count())
122+
self.assertEqual(["Scott", "Isaac"], [x.name for x in Employee.objects.all().order_by('id')])
123+
124+
def test_skip_creates(self):
125+
c1 = Company.objects.create(name="My Company LLC")
126+
127+
e1 = Employee.objects.create(name="Scott", age=40, company=c1)
128+
e2 = Employee.objects.create(name="Isaac", age=9, company=c1)
129+
130+
# create a new employee that will be ignored
131+
new_objs = [
132+
Employee(name="John", age=52, company=c1)
133+
]
134+
135+
ret = bulk_sync(new_models=new_objs, filters=None, key_fields=("name",), skip_creates=True, skip_deletes=True)
136+
137+
self.assertEqual(2, Employee.objects.count())
138+
self.assertEqual(["Scott", "Isaac"], [x.name for x in Employee.objects.all().order_by('id')])
139+
140+
def test_skip_updates(self):
141+
c1 = Company.objects.create(name="My Company LLC")
142+
143+
e1 = Employee.objects.create(name="Scott", age=40, company=c1)
144+
e2 = Employee.objects.create(name="Isaac", age=9, company=c1)
145+
146+
# create a new employee that will be ignored
147+
new_objs = [
148+
Employee(name="Scott", age=100, company=c1)
149+
]
150+
151+
ret = bulk_sync(new_models=new_objs, filters=None, key_fields=("name",), skip_updates=True)
152+
153+
# the age should not have been updated
154+
new_e1 = Employee.objects.get(id=e1.id)
155+
self.assertEqual(40, new_e1.age)
156+
157+
158+
107159
class BulkCompareTests(TestCase):
108160
""" Test `bulk_compare` method """
109161

0 commit comments

Comments
 (0)