-1

I have subclassed built-in model Fields to reduce repetition in similar columns. This triggers exceptions in tests against Django 3.2 (but interestingly does work in the otherwise now irrelevant, unsupported, version 2.2)

django.core.exceptions.FieldError: Expression contains mixed types: DecimalField, DecimalFWB. You must set output_field.

from django.db.models import Model,DecimalField,F
from decimal import Decimal

class DecimalFWB(DecimalField):
    @property
    def validators(self):
        return super().validators + [MinValueValidator(0.1), ]
    ...

class Repro(Model):
   frac = DecimalFWB(max_digits=4, decimal_places=4, default=Decimal("0.2"))
   ...

# same internal type
assert DecimalFWB().get_internal_type() == DecimalField().get_internal_type()

# 3.2: works
# 2.2: works
Repro.objects.annotate(dec_annotation = -F("frac") + Decimal(1)).first()

# 3.2: django.core.exceptions.FieldError
# 2.2: works
Repro.objects.annotate(dec_annotation = Decimal(1) - F("frac")).first()

I found this entry in the Django 3.2 release notes that could explain the change in behaviour from the earlier version:

[..] resolving an output_field for database functions and combined expressions may now crash with mixed types when using Value(). You will need to explicitly set the output_field in such cases.

That suggestion does not solve my problem. If I were to bloat all annotations with ExpressionWrapper/output_field=, I could just as well bloat the model definition and not use the subclass in the first place.

I am trying to emulate the internal type. I want the combined output_field of DecimalField and DecimalFWB to be DecimalField - regardless of order of super/subclass. How do I express that no mixing is happening here?

anx
  • 174
  • 6

1 Answers1

0

Automatically selecting the shared field as the output has been fixed as of Bug #33397, released in Django 4.1 (but not backported). The change does however comes with a warning (emphasis mine):

As a guess, if the output fields of all source fields match then simply infer the same type here.

This guess is mostly a bad idea, but there is quite a lot of code (especially 3rd party Func subclasses) that depend on it, we'd need a deprecation path to fix it.

Meaning this might change again in a future release, but at least then intentionally, and reliably triggering a DeprecationWarning on request.

anx
  • 174
  • 6