2

I have the following abstract class:

class BaseClass(models.Model):
 @property
 def is_a_null(self):
  return False

 field_a = models.CharField(max_length=1, null=is_a_null)

 class Meta:
  abtract = True

If I derive from this class, the generated SQL for field_a does not have NOT NULL next to the field. I want to do it this way because I want a derived class to be able to override is_a_null.

If I put null=False instead of the function call, the generated SQL has NOT NULL next to the field as expected.

user2233706
  • 6,148
  • 5
  • 44
  • 86

2 Answers2

0

Unfortunately your approach is not going to work.

The "magic" of properties and descriptors happens when they are accessed as attributes. If you refer to them directly—for instance, during the class definition—you just get a property object.

class PropertyTest(object):
    @property
    def is_a_null(self):
        return False

    print(is_a_null)
    print(bool(is_a_null))

<property object at 0x02C0D5A0>
True

>>> PropertyTest().is_a_null
False

So, you are passing the is_a_null property object to the constructor for CharField, where it evaluates as True.

Even if it was accessing the property's getter as you desire, that still wouldn't work with inheritance. The subclass simply inherits the already-defined field; you could never count on it re-acccessing your property.

If Django's model inheritance allowed for redefining fields, you could simply create a new field_a attribute in the subclass definition. Unfortunately, that is not currently possible (though this discussion raises the possibility that this feature will soon be added).

You could adapt the patch in that thread, or create your own metaclass from scratch, though those will both be somewhat complex solutions.

Kevin Christopher Henry
  • 46,175
  • 7
  • 116
  • 102
0

I ended up following the answer in this question. For example:

class BaseClass(models.Model):
 field_a = models.CharField(max_length=1)

 class Meta:
  abtract = True

class DerivedClass(BaseClass):
 pass;

DerivedClass._meta.get_field('field_a').null = True

Would be nice if there was a way to do this without accessing _meta.

Community
  • 1
  • 1
user2233706
  • 6,148
  • 5
  • 44
  • 86