Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#31683 closed Bug (invalid)

Overridden methods ignored in models.DecimalField subclass

Reported by: Max Rothman Owned by: nobody
Component: Database layer (models, ORM) Version: 2.2
Severity: Normal Keywords: custom-model-fields
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Django: 2.2.4
Python: 3.6.9
Mysql: 5.6.47

It appears to be impossible to override any methods in subclasses of models.DecimalField under certain circumstances. Consider the following:

class Field1(models.DecimalField):
    def get_prep_value(self, value):
        1/0

class Field2(models.PositiveIntegerField):
    def get_prep_value(self, value):
        1/0

class TestModel1(models.Model):
    m = Field1(max_digits=5, decimal_places=2)

class TestModel2(models.Model):
    m = Field2()

TestModel1(m=1).save()  # No error!
TestModel2(m=1).save()  # Error, as expected

I'd expect to get an error when saving TestModel1 because get_prep_value should throw a ZeroDivisionError, but as far as I can tell, Field1.get_prep_value is never called.

This bug appears to be somewhat sensitive to platform conditions. I've been able to replicate it consistently on my machine, but another one of my colleagues who tried was not.

Change History (5)

comment:1 by Mariusz Felisiak, 4 years ago

Resolution: invalid
Severity: Release blockerNormal
Status: newclosed

Overriding works fine. DecimalField doesn't call get_prep_value() on .save() because it has a custom get_db_prep_save() implementation:

>>> Field1().get_prep_value('asdasd')
ZeroDivisionError: division by zero
>>> TestModel1.objects.filter(m='3.44')
ZeroDivisionError: division by zero

comment:2 by Max Rothman, 4 years ago

The docs do not mention anywhere that get_prep_value is expected to be called in get_db_prep_value on save, nor that DecimalField behaves differently from most other fields in this respect. Could this be reopened as a documentation bug?

in reply to:  2 comment:3 by Mariusz Felisiak, 4 years ago

The docs do not mention anywhere that get_prep_value is expected to be called in get_db_prep_value on save,...

It's not required, I just wanted to explain why exception is raised in .save() for PositiveIntegerField.

...nor that DecimalField behaves differently from most other fields in this respect. Could this be reopened as a documentation bug?

I don't think so, we cannot document implementation details of all builtin fields.

comment:4 by Max Rothman, 4 years ago

So as a django user the way I was supposed to find this out was to read the source code? The fact that this issue exists is proof that there is at least some kind of documentation issue.

in reply to:  4 comment:5 by Mariusz Felisiak, 4 years ago

Replying to Max Rothman:

So as a django user the way I was supposed to find this out was to read the source code?

Writing a custom model field is quite advanced usage. You can create a subclass of Field and implement methods described in the How-to. If you want to create a subclass of the builtin fields you can also implement all methods or check an implementation and use relations between them. I don't see any contradiction.

The fact that this issue exists is proof that there is at least some kind of documentation issue.

I don't see any issue here. If you want to raise an exception when converting values to database values you should override get_db_prep_value() as documented.

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