0

I'm using signals in models.py but when I make a sum this function does it twice instead of just a sum

models.py:

class Articulo(models.Model):
cod_experto = models.CharField(max_length=999, primary_key=True, blank=True)
nombre      = models.CharField(max_length=999, blank=True)
descripcion = models.CharField(max_length=999, blank=True, null=True)
on_delete=models.CASCADE)
stock       = models.IntegerField(blank=True, default=0)
total_pedido =models.IntegerField(blank=True, default=0)

class Pedido(models.Model):
especialidad   = models.ForeignKey('Especialidad')
articulo       = models.ForeignKey('Articulo')
blank=True)
cantidad       = models.IntegerField(blank=True)    default='pendiente')

 def __str__(self):
return '{}'.format(self.especialidad, self.articulo, self.cantidad,   self.estado)

def update_total(sender, instance, **kwargs):
   instance.articulo.total_pedido += instance.cantidad
   instance.articulo.save()

# register the signal
signals.post_save.connect(update_total,sender=Pedido, dispatch_uid="update_stock_count")

views.py

def Cant_ingresar(request, id_pedido, id_especialidad):
especialidad = Especialidad.objects.get(id=id_especialidad)
pedido = Pedido.objects.get(id=id_pedido)
if request.method == 'GET':
  form = PedidoEditForm(instance=pedido)
else:
  form = PedidoEditForm(request.POST, instance=pedido)
  if form.is_valid():
       form.save()
       """
       pedido.estado = 'pendiente'
       pedido.fecha_pedido = datetime.date.today()
       pedido.save()
       especialidad.estado='pendiente'
       especialidad.save()
       """
  return HttpResponseRedirect('/solicitar/lista_active/%s/' % id_especialidad)
return render(request, 'form.html', {'form':form, 'pedido':pedido, 'especialidad':especialidad, 'pedido':pedido}) 

As you can see, I first save the cantidad entered in the def of views.py, then in the model with the signals captured with post_save the amount and is summed with total_pedido of the Articulo model, this without problems, but adds two Times the same amount ie, enter a 3 and this goes to total_pedido as 6.

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • Take a look at [this](http://stackoverflow.com/questions/12012887/django-post-save-signal-getting-called-twice-despite-uid). It might be what you're looking for. – nik_m Mar 23 '17 at 11:45
  • can your format your code plase? – e4c5 Mar 23 '17 at 11:47

2 Answers2

0

Assuming this double update happens when the currently commented out code is not commented out :

if form.is_valid():
   # this will call `pedido.save()` once
   form.save()
   # ...
   # adds some things to pedido...
   # ...
   # and here you're saving pedido a second time
   pedido.save()

So no suprise you get your signal handler called twice.

There are solutions to avoid this double save call (like passing commit=False to form.save()) or (like suggested by itzmeontv) moving this update from a post_save signal to pedido.save() - but this won't fix the underlying logical flaw: this code will be executed each and every time Pedido.save() is called whatever the reason (like updating another unrelated field), each time adding self.cantidad over and over and over to self.articulo.total_pedido.

Here you have two solutions: either explicitely update self.articulo when you know it's needed (but this may not be very reliable), or instead of blindly adding to articulo.total_pedido ask articulo to completely recompute its value from scratch, ie:

from django.db.models import Sum

class Articulo(models.Model):
   # ....

   def update_total_pedido(self):
       result = self.pedido_set.aggregate(total=Sum('cantidad'))
       self.total_articulo = result['total']
       self.save(update_fields=['total_articulo'])

and then in Pedido:

from django.db import transaction

class Pedido(models.Model):
    # ...
    def save(self, *args, **kw):
        with transaction.atomic():
            super(Pedido, self).save(*args, **kw)
            self.articulo.update_total_pedido()

Also note that if nothing prevents Pedido from being deleted, you may want to call Articulo.update_total_pedido() on deletion too.

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
-1

Instead of post_save override save

class Pedido(models.Model):
    .....

    def save(self, *args, **kwargs):
        self.articulo.total_pedido += self.cantidad
        self.articulo.save()
        super(Pedido, self).save(*args, **kwargs) 
itzMEonTV
  • 19,851
  • 4
  • 39
  • 49
  • I understand your point, although this example does not add anything to my total_pedido –  Mar 23 '17 at 12:24
  • Updated the views.py I think it's because I have more than one .save () according to your answer? –  Mar 23 '17 at 12:28
  • I already tried it! Thanks to your answer I cleared my mind, thank you, sir! thank you very much! –  Mar 23 '17 at 12:30
  • This will STILL mindlessly update `self.articulo.total_pedido` _each and every time_ `Pedido.save()` is called. – bruno desthuilliers Mar 23 '17 at 12:46
  • @itzmeontv sorry but no cigar - there's no recursion involved here, it's `Articulo.save()` that's getting called in the signal handler, not `Pedido.save()`. If that was the case the OP would get an infinite recursion. – bruno desthuilliers Mar 23 '17 at 13:12