2

The use case :

I have a form object which receives instance variables (form fields). Now I like to validate there variables using method chaining. An example with arbitrary methods :

class Field(object):

    def __init__(self, form, name):
        self.form = form
        self.name = name

    def unspace(self):
        setattr(self.form, self.name, getattr(self.form, self.name).replace(' ',''))
        return self

    def len_valid(self, length):
        if len(getattr(self.form, self.name)) < length :
            setattr(self.form, self.name + '_invalid', True)
            self.form.valid = False
        return self

class Forms(object):                                                                                    

    def __init__(self):
        self.valid = True

    def validate(self, name):
        return Field(self,name)

f = Forms()         # create the form with some data
f.a = 'J o Hn '
f.b = ' Too   L o n g'

f.validate('a').unspace().len_valid(2)  
f.validate('b').unspace().len_valid(5)  

RESULT :
f.a : 'JoHn'
f.a_invalid : True
f.b : 'TooLong'
f.valid : False

Is this the Pythonic way to create method chaining on the Form instance variables.

2
  • Not a direct answer, but: is there a reason these things have to be attributes (instance variables) instead of just members of a dict, so you can drop all the setattr/getattr stuff and write simple, direct code? Commented Dec 12, 2012 at 18:28
  • You're making this way too complicated. Do what @abarnert says and just put everything in a dict. Commented Dec 12, 2012 at 18:29

1 Answer 1

4

Yes and no.

The Pythonic way to chain method calls is exactly what you've written:

f.validate('a').unspace().len_valid(2)  

But the Pythonic way to access attributes dynamically is to not do that unless you have to. If the form variables were stored in a dict instead of as instance variables of the object, everything would be much simpler and more readable.

Even if you really need the form variables to be accessible as f.a instead of f['a'] (e.g., because this is part of an interactive shell, or some third-party API requires that), it's actually easier to write all your code around a dict, and use your favorite AttrDict class (from PyPI or ActiveState) to provide the attribute-style access for your user/third-party API.

Also, if you further changed the Field object to be a trivial wrapper around a value with some methods, instead of (in effect) a reference to a parent and a key, it would be even simpler.

Also, if you generate new attributes on the fly like a_invalid, you probably want to always generate them, not only when they're true. Otherwise, checking whether a is valid looks something like this:

try:
    avalid = not f.a_invalid
except NameError:
    avalid = True

That's horribly convoluted, but if your caller wants to avoid that, the only way to do so is something like this:

avalid = not getattr(f, 'a_invalid', False)

Which seems to defeat the whole purpose of faking attributes for the caller in the first place.

Also, keep in mind that you have to make sure that there can never be a field whose name ends with _invalid. Since you can attach new attributes to almost anything in Python, so if you really do want to do everything this way, why use f.a_invalid instead of, say, f.a.invalid?

Since you asked in the comments, a trivial wrapper around a value looks like this:

class Field(object):

    def __init__(self, value):
        self.value = value
        self.valid = True

    def unspace(self):
        self.value = self.value.replace(' ', '')
        return self

    def len_valid(self, length):
        if len(self.value) < length:
            self.valid = False
        return self

Instead of making each field reach up to the Form to set its validity, just make the Form do it:

class Form(object):
    …
    def valid(self):
        return all(field.valid for field in self.fields)

And if you really need to make that valid look like a member variable rather than a method, just use @property.

Sign up to request clarification or add additional context in comments.

12 Comments

Or you could just use an existing form validation library.
@Marcin : The form validation is part of a form wizzard. This is why i did not use a validation library.
@abarnert. Thanks for your great comment. I use names with _invalid for my Jinja templates, because I can remove them easy. Can you give me an example of your "trivial wrapper". By the way : I switched from a dict to instance variables to make it more readable. With your suggestions like AttrDict I will give it another try.
@voscausa I have no idea why that would be a reason not to use an existing library for this task.
@Marcin: Except, of course, that you might want to use an existing form wizard library that had its own validation system, so then you wouldn't need a separate form validation library. :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.