jaimegildesagredo / booby

Data modeling and validation Python library
https://booby.readthedocs.org
Other
176 stars 18 forks source link

Model attributes are being set on the class, not the instance. #8

Closed erikcw closed 11 years ago

erikcw commented 11 years ago

The docs suggest using an empty list for the default value of a field with a list of models as its validator.

Unfortunately, this will lead to undesirable behavior if the user tries to append models to an instance of the model containing the list field.

Background: http://effbot.org/zone/default-values.htm

from booby import models, fields, validators

class A(models.Model):
    a = fields.StringField(choices=['a1', 'a2',])

class B(models.Model):
    boolean = fields.BooleanField(default=False)
    a_list = fields.Field(validators.List(validators.Model(A)), default=[])
    a_not_list = fields.EmbeddedField(A)

From IPython:

In [7]: b = B()

In [8]: b.a_list.append(A(a='a1'))

In [9]: b.to
b.to_dict  b.to_json  

In [9]: b.to_dict()
Out[9]: {'a_list': [{'a': 'a1'}], 'a_not_list': None, 'boolean': False}

In [10]: b = B()

In [11]: b.to_dict()
Out[11]: {'a_list': [{'a': 'a1'}], 'a_not_list': None, 'boolean': False}

In [12]: b.a_list.append(A(a='a2'))

In [13]: b.to_dict()
Out[13]: {'a_list': [{'a': 'a1'}, {'a': 'a2'}], 'a_not_list': None, 'boolean': False}

Notice how the output from In[11] contains [{'a': 'a1'}] instead of the anticipated [].

Ideally and ListField would be added to the library as a work around. But at the very least the docs should be changed so that this isn't promoted.

jaimegildesagredo commented 11 years ago

Hi @erikcw!

Thanks for report the issue! Although it isn't really a bug in Booby, but rather a tricky behavior of the python's arguments default values, as explained in your link.

Anyway this should be fixed in some way in Booby, so I just added support to pass callable objects as a field default argument. When using callables as default, the default value of the field will be the callable return value.

Now, for example, you could do something like Field(default=list) or Field(default=lambda: []) to achieve the expected behavior.

I think this is the clearest way to solve that problem. Anyway, suggestions are welcome ;)