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

3k views Asked by At

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.

4

There are 4 answers

0
drdaeman On BEST ANSWER

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.

0
Matthias Urlichs On

You can teach FormAlchemy to not replace an empty input with None by changing the null_as column attribute.

 whatever = Column(Unicode(999), required=False, null_as=("","\0"), nullable=False, default="", doc="Whatever for ever")

Formalchemy will replace input that's equal to the second member of the null_as tuple with None. (The first is the display text for SELECTs and similar fields.)

If you set that to a string the user cannot input, this will never happen.

1
Wolph On

Can you explain why nullable=True would not be the solution?

To me it seems useless to store empty strings in the database and I would not encourage it. If there is no data than choose the more efficient type for the database.

Personally I think the Django solution in case of strings is wrong since it does not really support NULL in CharFields. If you want to store NULL in a CharField you will have to do it manually in the code.

0
Damian On

This is exactly the same issue i'm having, where I often write interfaces for existing databases and want to use formalchemy but end up having to manually remove 'requiredness' as I am not able to change the database.