goinnn / django-multiselectfield

A Multiple Choice model field
GNU Lesser General Public License v3.0
449 stars 208 forks source link

The 0.1.13 release triggers an error in `MaxLengthValidator` in Django 3.2.25 #157

Closed austin-schick closed 2 months ago

austin-schick commented 2 months ago

The README indicates that django-multiselectfield supports Django 2.0+. If that is no longer true and I should fix this bug by upgrading Django, that's understandable!

Report

After upgrading to django-multiselectfield 0.1.13, when modifying objects via the admin page changeform, I'm seeing an exception in django/core/validators.py .

I think the error was introduced in this commit: https://github.com/goinnn/django-multiselectfield/commit/422fc71b4c772feb3483aec5d60573eedf960482:

-self.validators[0] = MaxValueMultiFieldValidator(self.max_length)
+self.validators.append(MaxValueMultiFieldValidator(self.max_length))

In Django 3.2.25, CharField unconditionally adds a validator when it's initialized:

class CharField(Field):
    description = _("String (up to %(max_length)s)")

    def __init__(self, *args, db_collation=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.db_collation = db_collation
        self.validators.append(validators.MaxLengthValidator(self.max_length))

That MaxLengthValidator doesn't run successfully on a MultiSelectField, so I get the error below.

Thanks for taking a look at this, and thanks for keeping the library up to date!

Stack Trace

Traceback (most recent call last):
  File "venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "venv/lib/python3.9/site-packages/django/contrib/admin/options.py", line 616, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "venv/lib/python3.9/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "venv/lib/python3.9/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "venv/lib/python3.9/site-packages/django/contrib/admin/sites.py", line 232, in inner
    return view(request, *args, **kwargs)
  File "backend/users/admin.py", line 499, in change_view
    return self.changeform_view(request, object_id, form_url, extra_context)
  File "venv/lib/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "venv/lib/python3.9/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "venv/lib/python3.9/site-packages/django/contrib/admin/options.py", line 1540, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
  File "venv/lib/python3.9/site-packages/django/contrib/admin/options.py", line 1579, in _changeform_view
    form_validated = form.is_valid()
  File "venv/lib/python3.9/site-packages/django/forms/forms.py", line 175, in is_valid
    return self.is_bound and not self.errors
  File "venv/lib/python3.9/site-packages/django/forms/forms.py", line 170, in errors
    self.full_clean()
  File "venv/lib/python3.9/site-packages/django/forms/forms.py", line 374, in full_clean
    self._post_clean()
  File "venv/lib/python3.9/site-packages/django/forms/models.py", line 413, in _post_clean
    self.instance.full_clean(exclude=exclude, validate_unique=False)
  File "venv/lib/python3.9/site-packages/django/db/models/base.py", line 1229, in full_clean
    self.clean_fields(exclude=exclude)
  File "venv/lib/python3.9/site-packages/django/db/models/base.py", line 1271, in clean_fields
    setattr(self, f.attname, f.clean(raw_value, self))
  File "venv/lib/python3.9/site-packages/django/db/models/fields/__init__.py", line 671, in clean
    self.run_validators(value)
  File "venv/lib/python3.9/site-packages/django/db/models/fields/__init__.py", line 623, in run_validators
    v(value)
  File "venv/lib/python3.9/site-packages/django/core/validators.py", line 361, in __call__
    if self.compare(cleaned, limit_value):
  File "venv/lib/python3.9/site-packages/django/core/validators.py", line 422, in compare
    return a > b

Exception Type: TypeError at /admin/users/user/0/change/
Exception Value: '>' not supported between instances of 'int' and 'NoneType'
mikemanger commented 2 months ago

There is some discussion of this change here. I think this can be worked around by manually setting max_length to be the largest field as my example shows here. Django <4.1 and Python < 3 support got dropped in #148 so you might still need to update.

austin-schick commented 2 months ago

Thanks, closing in favor of a new issue about the outdated README instead :)

https://github.com/goinnn/django-multiselectfield/issues/159