in src/olympia/amo/utils.py [0:0]
def set_max_length_on_fields_where_necessary(self):
"""
Automatically set max_length and associated validator on fields that
map to a model field where it's already set.
Allow declaring custom fields without having to re-declare the same
max_length for those fields.
Should be called at __init__() time."""
for field_name, field in self.fields.items():
if getattr(field, 'read_only', False):
continue
source = getattr(field, 'source', field_name)
if getattr(field, 'max_length', None) is None and self.Meta.model:
try:
model_field = self.Meta.model._meta.get_field(source)
except FieldDoesNotExist:
continue
if (max_length := getattr(model_field, 'max_length', None)) is not None:
field.max_length = max_length
# Normally setting max_length on the field would be enough,
# but because we're late, after the field's __init__(), we
# also need to:
# - Update the widget attributes again, for form fields
if hasattr(field, 'widget'):
field.widget.attrs.update(field.widget_attrs(field.widget))
# - Update the sub-widgets too if there are any (our
# translations widgets work like that)
if hasattr(field.widget, 'widgets'):
for widget in field.widget.widgets:
widget.attrs.update(field.widget_attrs(widget))
# - Convert that max_length into a validator ourselves if
# the field requires it. Unfortunately some fields
# (FileField) do not work like that and instead deal with
# max_length dynamically, with a custom error message
# that is generated dynamically, so we need to avoid
# those. If a compatible error message for max_length is
# set, or no message at all, we're good.
message = getattr(field, 'error_messages', {}).get('max_length')
if message is None or re.findall(r'\{.*?\}', str(message)) == [
'{max_length}'
]:
if message:
message = lazy_format(
field.error_messages['max_length'],
max_length=field.max_length,
)
field.validators.append(
MaxLengthValidator(field.max_length, message=message)
)