python-pyfields icon indicating copy to clipboard operation
python-pyfields copied to clipboard

Validate after setting multiple fields at once

Open bharathbhushan1 opened this issue 5 years ago • 3 comments

The requirement is to set multiple fields which have individual validators where there could be a period where the invariants are not satisfied but at the end of the "transaction", all validators are satisfied. Is there a way to get this done? If not, can we add this as an enhancement?

class Test(object):
  a = field(type_hint=six.integer_types, native=False, default=2)
  b = field(type_hint=six.integer_types, native=False, default=1)

  @a.validator()
  def validate_a(self, new_a):
    return new_a == self.b * 2

  @b.validator()
  def validate_b(self, new_b):
    return new_b == self.a / 2

t = Test()
# Start transaction
t.a = 100   # Fails because the invariant is not satisfied
t.b = 50
# End of transaction

bharathbhushan1 avatar Jun 27 '20 03:06 bharathbhushan1

This is a great suggestion ! It seems to connect with similar ideas/suggestions #8 and #25

I do not remember if I already wrote something in the code to prepare this, I'll need to investigate.

Anyway a context manager would seem quite appropriate to disable validation temporarily and set it back on exit:

with validate_once(t):
    t.a = 100
    t.b = 50

Maybe we could think about alternate names or additional complementary helpers, such as

  • with one_validation(t) could be an alternate name to the above

  • with no_validation(t) could be a second symbol, that would not validate at all on exit, as opposed to the above.

  • other names.... ? with direct_access(t), ...

What do you think ?

smarie avatar Jun 29 '20 08:06 smarie

I've just started looking at pyfields as an alternative to marshmallow, with the hopes of using it for convenient form and API parameter validation while providing a simple object with fields representing the submitted values. For me the ability to validate all values at once, providing a Flask form object (or some other dict) as input to my constructor, would be particularly helpful -- especially if I could get a marshmallow-like error result out of the process, mapping fieldnames to lists of error messages for those fields that failed validation. Hope this makes sense... been learning a lot this afternoon. Thanks!

jgarbers avatar Nov 03 '20 20:11 jgarbers

Thanks @jgarbers for your feedback ! Your message does not really seem to relate to this issue #72 - @bharathbhushan1 meant validation that can not be performed field by field but once all fields have been set. So I would recommend that you open a new dedicated issue.

To give a first level of answer, you can already validate all values at once by passing a **dct to the constructor ; however only the first invalid field will appear in the error message as opposed to what you seem to require.

Maybe this should be solved in a different function that we could generate optionally on the class ? or we could imagine a boolean flag triggering this in the constructor. Or maybe we could make this the default behaviour of the generated constructor ? I have no strong opinion here but performance might be something to consider.

smarie avatar Nov 09 '20 21:11 smarie