Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Update multiple objects attributes

Is there a more efficient way of do this maybe with F expressions? Some how to reducing hitting the DB?

# 1st way hits DB twice per object
def something():
  pks = [4, 2, 1, 3, 0]
  for i in range(len(pks)):
    mymodel.objects.get(pk=pks[i]).update(attr=i)

# 2nd way hits DB once per obj and once for the queryset
def something():
  pks = [4, 2, 1, 3, 0]
  order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(pks)])
  query = mymodel.objects.filter(pk__in=pks).order_by(order)
  for i in range(len(query)):
    query[i].attr = i
    query[i].save()

Editing to move variables consistent.

like image 440
Edward Park Avatar asked Dec 20 '16 23:12

Edward Park


2 Answers

Generally making multiple database queries instead of one large query is to be avoided. However your case seems to be an exception. (Note: I am making this answer based on the information provided. Please don't edit your question to say I actually meant ....)

The following query will be very fast no matter how many records you have because it's retrieving a single item from a primary key. All RDBMS are designed to handle this really well.

mymodel.objects.get(pk=a[i]) 

Hower you can make your function more efficient like this:

def something():
  pks = [4, 2, 1, 3, 0]
  for i in range(len(a)):
    mymodel.objects.filter(pk=a[i]).update(attr=i)

Now it hits the DB only once per object. the above query just translates to

 UPDATE mapp_mymodel SET attr=i where pk=1
like image 80
e4c5 Avatar answered Sep 29 '22 07:09

e4c5


If I'm not mistaken, you can do something like

pks = [4, 2, 1, 3, 0]
mymodel.object.filter(pk__in=pks).update(attr='test')

Unless you are trying to use the for loop index as the attribute value, in which this does not work.

like image 28
zubhav Avatar answered Sep 29 '22 07:09

zubhav