Skip to content

How to dynamically change Django Form field type (e.g. `forms.CharField` to `forms.ChoiceField`) without changing the data member variable?

TL;DR

How can I change the search term CharField (of any of the rows in the image below) to another field type (e.g. ChoiceField, DateField, etc) based on the type of the selected database field (in the first select list on that row – see screenshot below)?

Long version

I have a cool hierarchical advanced search interface, e.g.:

enter image description here

Each row specifies a search term/condition and reads as a search, like:

<field> <condition> <term>

e.g.

age > 5

where the form field names are:

  • fld (for “database field”)
  • ncmp (for “negatable comparison type”)
  • val (for the search term)

There is a hidden field called pos for the hierarchy and group type data, but that’s irrelevant to my question.

Rows and subgroups can be dynamically added/removed to/from the hierarchy and can be and-ed or or-ed together.

It works great. But the problem I want to solve is that it is somewhat limited by field type. I would like to be able to dynamically change the contents of the condition select list (ncmp) and the type of search term field (val) based on the selected database field (fld) (or the selected condition, e.g. if isnull: hide the term field). Here are some examples of what I want:

  1. If the selected field is a timestamp, I’d like the condition list to have selections like before/after/on day/etc and the term field to be a “timestamp” field with like a calendar widget or something
  2. If the field is a number type DB field, remove the contains/ends with/starts with/etc condition items and use a number validator on the term field
  3. If the field is an enumeration DB field, populate the condition select list with is/is not/is null/is not null/etc and make the term field a select list

Are there any standard ways to do this? Ideally, the form would still only have the 3 fields (fld, ncmp, and val) so that I wouldn’t have to overhaul the hierarchy javascript that controls the formsets, but I can do that if necessary.

Answer

I don’t have a completely comprehensive answer to this yet, but I have implemented ways to:

  1. update the ncmp select list based on the selected value in the fld select list.
  2. change the val form field type between a text field (for number/string database fields), a select list (for enumerated/”choices” database fields), and hidden (when ncmp is something like “is null” or “is not null”).

I haven’t figured out a way to use different Django widgets and accomplished the above using javascript. There are a few things to keep in mind:

  • The Django form class must define the ncmp field with all possible values regardless of which fld is currently selected and the javascript simply repopulates the select list on the subset corresponding to the current fld selection.
  • For the val field, I changed the Django form class’s val field to be a hidden field, and render a number of initially hidden form fields in javascript on each row, which I hide/show based on the current fld and ncmp value. Every javascript copy of the val field updates the value of the always hidden val field.

I imagine that any other representation of the val field would have to also be done in javascript and must be able to update a single hidden val field. So if you had multiple javascript-generated fields to enter the val (e.g. 3 text entries for a phone number or social security number), the javascript would have to condense that into a single string to update the hidden val field.