Dear Django users,
This is my first post on this ML, please excuse me if my question sounds weird.
My question is about the separation of roles between Widget and Field, in the context of eg. a serialization/deserialization of a list of values using JSON. It concerns especially the "Field.to_python", "Field.clean", "Field.bound_data", "Widget.render" and "Widget.value_from_datadict".
More concretely, I want to manipulate a list of DB entries in a Hidden field of a form, use some Javascript to populate the Hidden field and send it back to the form. Until today, I was using
* the to_python method to deserialize the JSON string that was arriving to the field, and to transform it to a proper list of objects,
* the prepare_value to initialize the JSON string to put into the Hidden input (non documented, this is the only way I found)
The problem was, after an erroneous form has been sent, the prepare_value was taking as input a non-deserialized JSON string. Example:
1* initial rendered input: <input id="json-for-list-authors" name="existing_authors" value="{"authors": []}" type="hidden">
2* after populating it with Javascript: <input id="json-for-list-authors"
name="existing_authors" value="{"authors":
["jean-philippe.XXX"]}" type="hidden">
3* sending the form with error renders the hidden input like this <input id="json-for-list-authors"
name="existing_authors" value="{"authors":
"{"authors":
["jean-philippe.vert"]"}" type="hidden"> (more or less some ").
It is then calling prepare_value on a JSON string received from 2/, and embedding a JSON into a JSON.
After that, I thought the right approach would be to override a specific Widget's "render" and "value_from_datadict": the transformation of the python list into a JSON is done in render, and the deserialization is done in value_from_datadict. I find that approach cleaner and neat:
* it solves my problem with having a proper serialization/deserialization in case of errors
* the Field.to_python does not concentrate in the actual representation of the data: it receives a list of values, regardless of the way I am serializing the data, and it concentrates on the logic of transforming the list or elements of this list into adequate python objects (eg. retrieving entries of the DB, removing duplicates)
* the widget is agnostic to logical errors (eg. entry does not exist in the DB): it concentrates on serializing/deserializing whatever object has been passed to it, and can concentrate of malformed string/data error (eg. unable to deserialize)
* there is no need to call a hidden API prepare_value
However, there are other problems:
* there is no proper way to unit test that: assertFieldOutput tests the result of the "clean", which is indirectly calling "to_python".
* In the case of list of values, since those are not hashable, they cannot be used as keys for the "valid"/"invalid" parameters of assertFieldOutput
* Some weird design issues appear: if for instance I would like to remove the duplicates in the "to_python", the removal of the duplicates will not affect the Widget, and the latter may render the duplicate values again in the form
* I would like to test the Widget (illformed strings) and the Field (inexisting users, duplicate removal) separately
The other option would be to use the BoundField.data or BoundField.value() to put the deserialization there. But then
* I would not be able to test it properly until I have a form.
*
To put everything in a nutshell, my question is: what is the best way to approach this kind of design? What are the responsibilities of those two/three entities?
Thanks,
Raffi