Skip to content

Commit

Permalink
Admin: fix and improve UI for downsample factors
Browse files Browse the repository at this point in the history
This UI has always been cumbersome, but recently broke entirely for
custom factors do to Django changes. Fix this and simplify the
interaction between field and widget here.

This is deceptively difficult because:
- MultiValueField and MultiWidget assume list (and *sometimes* tuple)
types are already decompressed. Downsample factors are a list of
Integer3D, and have to be so elsewhere for use as an ArrayField.
- SimpleArrayField also makes assumptions about coupling with its
default widget.
- The SimpleArrayFields used are of Integer3DFormField, which is another
MultiValueField/MultiWidget of list type.

See discussion in #1827.
  • Loading branch information
aschampion committed Feb 16, 2019
1 parent 28b6477 commit ab8fd48
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 9 deletions.
18 changes: 15 additions & 3 deletions django/applications/catmaid/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ def formfield(self, **kwargs):
def planar_default(num_zoom_levels):
return [Integer3D(2**l, 2**l, 1) for l in range(num_zoom_levels + 1)]

@staticmethod
def is_value(value):
return isinstance(value, list) and all([isinstance(l, Integer3D) for l in value])

# ------------------------------------------------------------------------

class Integer3DFormField(forms.MultiValueField):
Expand Down Expand Up @@ -355,14 +359,19 @@ def __init__(self, *args, **kwargs):
widget=forms.RadioSelect),
forms.IntegerField(label='Number of zoom levels'),
SimpleArrayField(
Integer3DFormField(),
# Must be disabled for Django to decompress str values during `clean`.
Integer3DFormField(disabled=True),
label='Factors array',
delimiter='|',
max_length=kwargs['max_length']),
)
del kwargs['max_length']
del kwargs['base_field']
super(DownsampleFactorsFormField, self).__init__(fields, *args, **kwargs)
# Because SimpleArrayField does not strictly adhere to Django conventions,
# our widget must have access to its field so that `prepare_value` can
# be used to convert the array to a string.
self.widget.array_field = self.fields[2]

def compress(self, data_list):
if data_list:
Expand All @@ -375,5 +384,8 @@ def compress(self, data_list):
return data_list[2]
return None

def prepare_value(self, value):
return (value, self.fields[2].prepare_value(value))
def clean(self, value):
if DownsampleFactorsField.is_value(value):
value = self.widget.decompress(value)

return super().clean(value)
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
<label for="widget.subwidgets.1.attrs.id">Number of zoom levels</label>
{% endif %}
{% if index == 2 %}
{% include widget.subwidgets.1.template_name with widget=widget.subwidgets.2 %}
{% include widget.subwidgets.2.template_name with widget=widget.subwidgets.2 %}
<label for="widget.subwidgets.2.attrs.id" class="help">
Delimit dimension factors by <code>,</code> and scale levels by <code>|</code>.
Example: <code>1,1,1|2,2,2|4,4,4</code>
</div>
{% endif %}
</li>
{% endfor %}
Expand Down
11 changes: 6 additions & 5 deletions django/applications/catmaid/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,17 @@ def __init__(self, attrs=None, **kwargs):
attrs, **kwargs)

def decompress(self, value):
if value is None or value[0] is None:
if value is None:
return [0, None, None]
elif value[0] == catmaid.fields.DownsampleFactorsField.planar_default(len(value[0]) - 1):
return [1, len(value[0]) - 1, value[1]]
elif value == catmaid.fields.DownsampleFactorsField.planar_default(len(value) - 1):
return [1, len(value) - 1, self.array_field.prepare_value(value)]
else:
return [2, len(value[0]) - 1, value[1]]
return [2, len(value) - 1, self.array_field.prepare_value(value)]

def get_context(self, name, value, attrs):
# Django doesn't play well with MultiValueFields/MultiWidgets whose
# normalization type is a list, so will not try to decompress list
# values by default.
value = self.decompress(value)
if catmaid.fields.DownsampleFactorsField.is_value(value):
value = self.decompress(value)
return super(DownsampleFactorsWidget, self).get_context(name, value, attrs)

0 comments on commit ab8fd48

Please sign in to comment.