Subquery has too many columns

132 views
Skip to first unread message

Elton Pereira

unread,
Feb 18, 2017, 5:28:16 PM2/18/17
to django...@googlegroups.com
When I change the base_manager_name and try to update the model an exception is raised.

This is my code:

class ProdutoServicoManager(models.Manager):
    def get_queryset(self):
        expression = models.Sum(models.F('custounitario__valor') * models.F('custounitario__quantidade'))
        total_expr = models.ExpressionWrapper(expression, output_field=models.DecimalField())
        return super().get_queryset().annotate(custo_producao=total_expr)

class ProdutoServico(Base):
    produto = models.BooleanField(default=True)
    descricao = models.TextField()
    objects = ProdutoServicoManager()

    class Meta:
        base_manager_name = 'objects'

I thought I had found the answer to this problem in issue 19513, but it was already closed. Apparently this error reappears when I try to annotate the base_manager.

Should I open a new issue?
--

Elton Pereira de Lima

-----------------------------

Quem nada dúvida, nada descobre.

Melvyn Sopacua

unread,
Feb 18, 2017, 7:20:03 PM2/18/17
to django...@googlegroups.com

On Saturday 18 February 2017 22:27:46 Elton Pereira wrote:

 

> class ProdutoServicoManager(models.Manager):

> def get_queryset(self):

> expression = models.Sum(models.F('custounitario__valor') *

> models.F('custounitario__quantidade'))

> total_expr = models.ExpressionWrapper(expression,

> output_field=models.DecimalField())

> return

> super().get_queryset().annotate(custo_producao=total_expr)

>

> class ProdutoServico(Base):

 

What's the manager on Base?

 

> produto = models.BooleanField(default=True)

> descricao = models.TextField()

> objects = ProdutoServicoManager()

>

> class Meta:

> base_manager_name = 'objects'

 

Cause this doesn't actually do anything, beside being explicit.

 

--

Melvyn Sopacua

eltonplima

unread,
Feb 20, 2017, 8:09:40 PM2/20/17
to Django users
Base class is abstract.

class Base(models.Model):
    plano
= models.ForeignKey(Plano)


   
class Meta:
       
abstract = True

Melvyn Sopacua

unread,
Feb 21, 2017, 7:01:50 AM2/21/17
to django...@googlegroups.com

On Monday 20 February 2017 17:09:40 eltonplima wrote:

> Base class is abstract.

>

> class Base(models.Model):

> plano = models.ForeignKey(Plano)

>

>

> class Meta:

> abstract = True

>

>

> base_manager_name

> <https://docs.djangoproject.com/en/1.10/ref/models/options/#base-manag

> er-name>

 

Ack, my brain kept reading "default_manager_name".

Still - without a ForeignKey this shouldn't break anything. It seems it warrents a new bug report, but I'm curious where things break. It looks like this model is used as an inline in the admin. Is that correct?

 

I cannot reproduce this with a simple test case:

 

models:

class CartEntryManager(models.Manager):
def get_queryset(self):
qs = super(CartEntryManager, self).get_queryset()
expression = models.F('quantity') * models.F('price')
wrapped = models.ExpressionWrapper(expression,
output_field=models.DecimalField(
max_digits=20, decimal_places=2))
return qs.annotate(amount=wrapped)


class Cart(models.Model):
created = models.DateTimeField(auto_now_add=True)


class CartEntry(models.Model):
item = models.CharField(max_length=32, primary_key=True)
quantity = models.PositiveSmallIntegerField()
price = models.DecimalField(max_digits=20, decimal_places=2)
cart = models.ForeignKey(Cart, on_delete=models.CASCADE,
related_name='items')
is_shipped = models.BooleanField(default=False)
objects = CartEntryManager()

class Meta:
base_manager_name = 'objects'

tests:

class CartTest(TestCase):
@classmethod
def setUpTestData(cls):
cart = models.Cart()
cart.save()
keyboard = models.CartEntry(
item='Komplete Kontrol S88',
quantity=2,
price='999.00',
cart=cart
)
mixer = models.CartEntry(
item='Traktor Kontrol S8',
quantity=1,
price='1199.00',
cart=cart
)
keyboard.save()
mixer.save()
cls.cart = cart
cls.keyboard = keyboard
cls.mixer = mixer

def test_entry_annotation(self):
self.assertEqual(self.keyboard.amount, 1998)

def test_base_manager(self):
from decimal import Decimal
total
= Decimal('0.00')
for item in self.cart.items.all():
total += item.amount

total
= total.quantize(Decimal('0.01'))
expect = Decimal('3197.00').quantize(Decimal('0.01'))
self.assertEqual(total, expect)

def test_base_manager_update(self):
self.cart.items.update(is_shipped=True)
shipped = self.cart.items.filter(is_shipped=True).count()
self.assertEqual(shipped, 2)

> On Saturday, February 18, 2017 at 9:20:03 PM UTC-3, Melvyn Sopacua wrote:

> > On Saturday 18 February 2017 22:27:46 Elton Pereira wrote:

> > > class ProdutoServicoManager(models.Manager):

> > >

> > > def get_queryset(self):

> > >

> > > expression = models.Sum(models.F('custounitario__valor') *

> > >

> > > models.F('custounitario__quantidade'))

> > >

> > > total_expr = models.ExpressionWrapper(expression,

> > >

> > > output_field=models.DecimalField())

> > >

> > > return

> > >

> > > super().get_queryset().annotate(custo_producao=total_expr)

> >

> > > class ProdutoServico(Base):

> > What's the manager on Base?

> >

> > > produto = models.BooleanField(default=True)

> > >

> > > descricao = models.TextField()

> > >

> > > objects = ProdutoServicoManager()

> > >

> > >

> > >

> > > class Meta:

> > >

> > > base_manager_name = 'objects'

> >

> > Cause this doesn't actually do anything, beside being explicit.

> >

> >

> >

> >

> > Melvyn Sopacua

 

--

Melvyn Sopacua

eltonplima

unread,
Feb 21, 2017, 7:37:13 PM2/21/17
to Django users
This is part of my real code but demonstrate the issue in pratice:

from django.db import models

class ProdutoServicoManager(models.Manager):
    def get_queryset(self):
        custo_unitario = models.F('custounitario__valor')
        quantidade = models.F('custounitario__quantidade')
        expression = models.Sum(custo_unitario * quantidade)
        custo_producao_expr = models.ExpressionWrapper(expression,
                                                       output_field=models.DecimalField())
        return super().get_queryset().annotate(custo_producao=custo_producao_expr)


class ProdutoServico(models.Model):
    produto = models.BooleanField(default=True)
    descricao = models.TextField()
    objects = ProdutoServicoManager()

    class Meta:
        #################################################
        # Comment the line below and the test pass.
        #################################################
        base_manager_name = 'objects'


class CustoUnitario(models.Model):
    produto_servico = models.ForeignKey(ProdutoServico)
    item = models.CharField(max_length=128)
    quantidade = models.PositiveIntegerField()
    valor = models.DecimalField(max_digits=10,
                                decimal_places=4,
                                verbose_name='Valor unitário')


class Faturamento(models.Model):
    produto_servico = models.OneToOneField(ProdutoServico)
    quantidade = models.PositiveIntegerField()
    preco_unitario = models.DecimalField(max_digits=10, decimal_places=2)

# We need only a single simple test to demonstrate this issue.

from django.test import TestCase
from django.db import utils

from model_mommy import mommy

from core import models

class FaturamentoTest(TestCase):

    def test(self):
        faturamento = mommy.make(models.Faturamento)
        produto = faturamento.produto_servico
        try:
            produto.save()
        except utils.OperationalError:
            self.fail("Whats wrong?")



Melvyn Sopacua

unread,
Feb 21, 2017, 8:16:04 PM2/21/17
to django...@googlegroups.com

While the testcase is simple, it is not the simplest test :)

 

Two things come to mind:

  1. If you get rid of the Sum(), does it work then?
  2. Saving the model with the base_manager_name should not use that manager as related manager. It is only used when a model with a foreign key to ProdutoServico is saved.

In your original exception, it looks like ProdutoServico extends a model in sumario.models and both override the save method:

 

/home/eltonplima/ssd/repository/planonegocios/marketing/models.py in save

       super().save(*args, **kwargs) ...

▶ Local vars

/home/eltonplima/ssd/repository/planonegocios/sumario/models.py in save

       super().save(*args, **kwargs) ...

 

Is it possible your problem is in one of those two?

eltonplima

unread,
Feb 21, 2017, 9:55:16 PM2/21/17
to Django users
I tried many things, only work if I remove the annotate or remove base_manager_name. But I need both!

The save method is not the problem, I removed.

If the last code I posted work, then, my production code works.

PS: Sorry if my English is not very good, I'm trying to improve it.

eltonplima

unread,
Feb 25, 2017, 11:32:14 PM2/25/17
to Django users
For me it's realy a bug.

I need to report them?
Reply all
Reply to author
Forward
0 new messages