Reputation: 2995
I have a function that does operations on a Django model instance, and I'm trying to break it up into sub operations that can be used independently. However, this means that I end up calling .save
on each change and trigger a DB request.
def subop1(instance):
instance.a1 = 1
instance.save()
def subop1(instance):
instance.a2 = 2
instance.save()
def subop1(instance):
instance.a3 = 3
instance.save()
def main_op(instance):
subop1(instance) # triggers 1 DB request
subop2(instance) # triggers 1 DB request
subop3(instance) # triggers 1 DB request
Is there any way to avoid having .save
triggering a DB request every time while still keeping each function independently functional? Maybe some way of deferring the .save
operations until some context manager exits and then saving all at once with 1 DB request.
Tried to read through transaction
documentation but I didn't find anything that fit.
Upvotes: 1
Views: 219
Reputation: 715
Method 1:
The instance is the same so you can do this:
def subop1(instance):
instance.a1 = 1
def subop1(instance):
instance.a2 = 2
def subop1(instance):
instance.a3 = 3
def main_op(instance):
subop1(instance)
subop2(instance)
subop3(instance)
instance.save() # triggers 1 DB request
You can do this because instance is an object and it is passed by refence and not by value.
So, if you edit an instance in a function, the changes also remain outside that method
Method 2:
Or you can do something like this:
from threading import Timer
class Object():
a1 = 0
a2 = 0
a3 = 0
def save(self):
print(self.a1)
print(self.a2)
print(self.a3)
def subop1(instance):
instance.a1 = 1
restart_timer(instance)
def subop2(instance):
instance.a2 = 2
restart_timer(instance)
def subop3(instance):
instance.a3 = 3
restart_timer(instance)
def main_op(instance):
subop1(instance)
subop2(instance)
subop3(instance)
timer = None
def restart_timer(instance):
global timer
if timer != None:
timer.cancel() # this delete last timer
timer = Timer(5.0, Object.save, [instance]) # This call the save method after 5 sec
timer.start() # This start the new timer
if __name__ == '__main__':
obj = Object()
main_op(obj)
Pay attention however that this is not the best way to do it because the garbage collector could eliminate the instance so use this method only if you are sure that the object will remain global all the time.
Otherwise you have to add this method to the object class.
def __exit__(self, exc_type, exc_value, traceback):
self.save()
Pay attention pt.2 In this case, your item will be saved whenever you dispose of it. So if you don't voluntarily save the object, it'll be saved anyway.
So the best method in the end is to do more transactions on the database.
Upvotes: 0
Reputation: 308869
You could you add an optional argument to your methods, for example:
def subop1(instance, commit=True):
instance.a1 = 1
if commit:
instance.save()
Then call your instances with commit=False
, and then call save()
manually.
def main_op(instance):
subop1(instance, commit=False)
subop2(instance, commit=False)
subop3(instance, commit=False)
instance.save()
Upvotes: 2
Reputation: 41
You can do in the following manner.
def subop1(instance):
instance.a1 = 1
def subop1(instance):
instance.a2 = 2
def subop1(instance):
instance.a3 = 3
def main_op(instance):
subop1(instance)
subop2(instance)
subop3(instance)
instance.save()
Upvotes: 0