Opened 8 years ago

Closed 7 years ago

Last modified 7 years ago

#15766 closed Bug (worksforme)

select_related() changes type of DecimalField

Reported by: Carsten Fuchs Owned by: nobody
Component: Database layer (models, ORM) Version: 1.3
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


As originally reported at

Using Django 1.3 with Python 2.6.5 on Ubuntu 10.04 and mod_wsgi, with Oracle database, I've just experienced a case where the use of select_related() changes the result type of a DecimalField in a related object from decimal.Decimal to float (which in turn breaks my application).

In detail, please consider the following models (unrelated fields omitted for clarity):

class Code(models.Model):
    id        = models.BigIntegerField(primary_key=True)
    grenzwert = models.DecimalField(null=True, max_digits=5, decimal_places=2, blank=True)

class Erfasst(models.Model):
    id   = models.AutoField(primary_key=True)
    code = models.ForeignKey(Code, db_column='code')

And the following shell session:

lori@keep-surfing:~/Zeiterfassung$ python shell
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from Zeiterfassung.Lori.models import *

>>> z = Erfasst.objects.get(id=2726498)
>>> z.code.grenzwert
>>> type(z.code.grenzwert)
<class 'decimal.Decimal'>
>>> z.code.grenzwert > Decimal(0)

>>> z = Erfasst.objects.select_related().get(id=2726498)
>>> z.code.grenzwert
>>> type(z.code.grenzwert)
<type 'float'>
>>> z.code.grenzwert > Decimal(0)

(The last line is the one that caused/causes the problems in my app...)

In the second part of the example with select_related(), I'd have expected that it returns type decimal.Decimal as in the first.

Python 2.7 seems to handle Decimal-float comparisons differently/better than 2.6, and the above would probably silently work with Python 2.7, but not 2.6.

Attachments (1) (1.9 KB) - added by Carsten Fuchs 7 years ago.
The file referred to in comment #2 to reproduce the issue.

Download all attachments as: .zip

Change History (6)

comment:1 Changed 8 years ago by Luke Plant

Component: UncategorizedDatabase layer (models, ORM)
Easy pickings: unset
Resolution: worksforme
Status: newclosed

I can't reproduce this. I'm using Oracle 10g Express Edition, instantclient 11.2. I tested with Python 2.5 and Python 2.6, Django trunk, and got the following results:

In [1]: import decimal

In [2]: from ticket15766.models import *

In [3]: c = Code(grenzwert=decimal.Decimal('10.00'), id=1)

In [4]:

In [5]: e = Erfasst(code=c)

In [6]:

In [7]: Erfasst.objects.get(id=1).code.grenzwert
Out[7]: Decimal("10.00")

In [8]: Erfasst.objects.select_related().get(id=1).code.grenzwert
Out[8]: Decimal("10")

In [9]: type(Erfasst.objects.select_related().get(id=1).code.grenzwert)
Out[9]: <class 'decimal.Decimal'>

If you can provide enough info for us to reproduce it, please do so and re-open this ticket.

Also, I notice that in my results, the value Decimal("10") given with select_related() is strictly speaking different from Decimal("10.00"). I don't have enough experience with Decimal to know if this is a material difference in itself. If it is, then you could re-open on that basis too. But the info to track down your exact problem would probably be useful anyway.

comment:2 Changed 7 years ago by Carsten Fuchs

Resolution: worksforme
Status: closedreopened

Ok, sorry for not having provided more info earlier.

Using the attached file, I can reproduce the issue with these steps:

carsten@thubi:~/Zeiterfassung$ python shell
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> from ticket15766.models import *
>>> import decimal

>>> c = Code(text="test #15766", grenzwert=decimal.Decimal('10.00'))
>>>     # No output, probably because it's a BigIntegerField instead of an AutoField.
>>> c = Code.objects.get(id=104)
>>> c.text
u'test #15766                                                 '

>>> m = Mitarbeiter(name="test", key="test2", code=c)

>>> s = Status(status="test #15766", statustext="#15766")

>>> k = Kostenstelle(name="test #15766")

>>> e = Erfasst(code=c, key=m, status=s, realkst=k)

>>> Erfasst.objects.get(id=2457639).code.grenzwert
>>> type(Erfasst.objects.get(id=2457639).code.grenzwert)
<class 'decimal.Decimal'>

>>> Erfasst.objects.select_related().get(id=2457639).code.grenzwert
>>> type(Erfasst.objects.select_related().get(id=2457639).code.grenzwert)
<type 'float'>

Note that Code is referenced by foreign key both from Erfasst.code as well as from Erfasst.key.code, maybe that is the cause of the issue?

I'd be very happy if you had another look at this. :-)

Changed 7 years ago by Carsten Fuchs

Attachment: added

The file referred to in comment #2 to reproduce the issue.

comment:3 Changed 7 years ago by Luke Plant

Resolution: worksforme
Status: reopenedclosed

I can't get past the line:

With Oracle:

IntegrityError: ORA-01400: cannot insert NULL into ("TEST"."CODE"."ID")

Which is what it must do. It could only succeed if your database actually is allowing a NULL primary key for the field, which isn't what the model says. If we've got one mismatch between what the model says and what the database allows, we may have others, perhaps including the field showing the bug. I don't think we are intending to support the case where the model disagrees with the database about what the field is.

So, if you can produce a minimal project that shows this issue when you create a database from scratch using syncdb etc, please post it and re-open. Thanks!

So you've got something else going on here.

comment:4 Changed 7 years ago by Carsten Fuchs

lukeplant, you are right:
I had to start my project with a legacy Oracle database, and used inspectdb to create the initial, and never realized that it had a mix of database fields of type FLOAT and NUMBER that in were both addressed with DecimalField(...).

Changing the database to use type NUMBER instead of FLOAT fixes my problem (even though the Oracle docs seem to indicate that FLOAT is "equivalent" to NUMBER...).

Please forgive me the false alarm, I'm very sorry for the trouble.
Many thanks for your patience and help!

comment:5 Changed 7 years ago by anonymous


Note: See TracTickets for help on using tickets.
Back to Top