Search code examples
pythonsqlalchemyvalidationpylonsformalchemy

Do not require non-NULL field (allow empty strings) in FormAlchemy


I'm fairly novice to FormAlchemy and it seems that I don't get something. I have a SQLAlchemy model defined like this:

...
class Device(meta.Base):
    __tablename__ = 'devices'

    id = sa.Column('id_device', sa.types.Integer, primary_key=True)
    serial_number = sa.Column('sn', sa.types.Unicode(length=20), nullable=False)
    mac = sa.Column('mac', sa.types.Unicode(length=12), nullable=False)
    ipv4 = sa.Column('ip', sa.types.Unicode(length=15), nullable=False)
    type_id = sa.Column('type_id', sa.types.Integer,
                        sa.schema.ForeignKey('device_types.id'))
    type = orm.relation(DeviceType, primaryjoin=type_id == DeviceType.id)
...

Then in my (Pylons) controller I create a FormAlchemy form like this:

c.device = model.meta.Session.query(model.Device).get(device_id)
fs = FieldSet(c.device, data=request.POST or None)
fs.configure(options=[fs.ipv4.label(u'IP').readonly(),
                      fs.type.label(u'Type').with_null_as((u'—', '')),
                      fs.serial_number.label(u'S/N'),
                      fs.mac.label(u'MAC')])

The documentation says that "By default, NOT NULL columns are required. You can only add required-ness, not remove it.", but I want to allow non-NULL empty strings, which validators.required disallows. Is there something like blank=True, null=False in Django?

To be more precise, I want a custom validator like one below, to either allow empty strings with type=None or all values to be set non-NULL and non-empty:

# For use on fs.mac and fs.serial_number.
# I haven't tested this code yet.
def required_when_type_is_set(value, field):
    type_is_set = field.parent.type.value is not None:
    if value is None or (type_is_set and value.strip() = ''):
        raise validators.ValidationError(u'Please enter a value')

If possible, I'd like to refrain from monkey-patching formalchemy.validators.required or other kludges. I don't want to set nullable=True on model fields, because it doesn't seems to be proper solution too.

What's the correct way to validate form in such case? Thanks for any suggestions in advance.


Solution

  • Finally found a kludge, but it seems this is the only sane way to do it:

      fs.serial_number.validators.remove(formalchemy.validators.required)
      fs.mac.validators.remove(formalchemy.validators.required)
    

    For the validator function, that FA will completely skip all validation when the value is None, because, by convention, it won't pass None to validators (except for when validators.required is set, which is hard-coded). I've filed an enhancement request ticket trying to solve this.