Opened 3 years ago

Last modified 12 months ago

#18480 new Bug

Inherited model fails to handle blank field properly

Reported by: yuval_a Owned by:
Component: Database layer (models, ORM) Version: 1.4
Severity: Normal Keywords:
Cc: navi7 Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I just opened a stack overflow question about this, but after digging a bit this is starting to look like a bug.

Take two simple models with inheritance:

class A(models.Model):
    a = models.IntegerField(blank=True)

class B(A):
    b = models.IntegerField(blank=True)

And then in runtime:

>>> A()
<A: A object>
>>> B()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "django/db/models/base.py", line 357, in __init__
    setattr(self, field.attname, val)
  File "django/db/models/fields/related.py", line 271, in __set__
    (instance._meta.object_name, self.related.get_accessor_name()))
ValueError: Cannot assign None: "B.b" does not allow null values.

There should be absolutely no reason why A() is properly created, while B() should fail.

Change History (7)

comment:1 Changed 3 years ago by yuval_a

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

The issue here is a conflicting field name. This is totally irrelevant to the field being blank.

I would argue, though, that the error must be much more descriptive. And I still don't quite understand why this isn't an issue with the parent model.

comment:2 Changed 3 years ago by aaugustin

  • Triage Stage changed from Unreviewed to Accepted

blank=True is irrelevant here; blank is validation-related and the example doesn't involve forms.

I reproduced the inconsistency and I agree that it's a bug.

I don't understand the comment above. yuval_a, are you sure you haven't misread the model definition?

comment:3 Changed 3 years ago by anonymous

Well, what's the bug here, in your opinion?

As far as I can tell I've made the error of having field names which are the same as model names in both examples.

It's odd that the error only reproduces on the sub-model. And the error should definitely be more descriptive.

comment:4 Changed 23 months ago by anonymous

Seems all related accessor name are not validated against existing fields.

class A(models.Model):

a = models.IntegerField()
br = models.IntegerField()

class B(models.Model):

a = models.ForeignKey(A, related_name='br')

raises an error.

Should fixing this wait till the validation changes hit?

comment:5 Changed 12 months ago by navi7

  • Owner changed from nobody to navi7
  • Status changed from new to assigned

comment:6 Changed 12 months ago by navi7

This is a kind of red herring.

The actual problem stems from the model coming up with the same field name as name of the derived class itself so it can have the reference to the base class. Model building then just overwrites our field. The simplest test is this:

class FruitFly(models.Model):
    pass

class CroatianFruitFly(FruitFly):
    croatianfruitfly = models.IntegerField()


fly2 = CroatianFruitFly()  #  this throws wrong exception

The error thrown is:

ValueError: Cannot assign None: "CroatianFruitFly.croatianfruitfly" does not allow null values.

And that's totally misleading.

As I'm totally new to Django, I don't know a way out of it. It seems that using the same field name as the class can be explicitly forbidden by throwing meaningful errors when the model is built. Then again, that could potentially be a breaking change.

Or, the name of the reference could be mangled somehow to not use that exact name.

Anyways, I need someone who knows the internals a bit more to look at this.

comment:7 Changed 12 months ago by navi7

  • Cc navi7 added
  • Owner navi7 deleted
  • Status changed from assigned to new
Note: See TracTickets for help on using tickets.
Back to Top