Валидация Django max_length для BinaryField вызывает KeyError в переводе __init__.py
У меня есть простая модель, что-то вроде
class Notenbild(models.Model):
bild_data = models.BinaryField(max_length=500000, editable=True)
В admin.py
class BinaryFieldWithUpload(forms.FileField):
def __init__(self, *, max_length=None, allow_empty_file=False, **kwargs):
super().__init__(max_length=max_length, allow_empty_file=allow_empty_file, **kwargs)
def to_python(self, data):
data = super().to_python(data)
if data:
image = Image.open(data)
# some more processing with the image which I omitted here
byte_array = io.BytesIO()
image.save(byte_array, format='PNG')
return byte_array.getvalue()
return None
def widget_attrs(self, widget):
attrs = super().widget_attrs(widget)
if isinstance(widget, FileInput) and "accept" not in widget.attrs:
attrs.setdefault("accept", "image/*")
return attrs
@admin.register(Notenbild)
class NotenbildAdmin(admin.ModelAdmin):
fields = [
'bild_data',
'vorschau',
]
readonly_fields = ['vorschau']
formfield_overrides = {
models.BinaryField: {'form_class': BinaryFieldWithUpload},
}
@admin.display(description='Bild (Vorschau)')
def vorschau(self, notenbild: Notenbild):
encoded_image = base64.b64encode(notenbild.bild_data).decode('utf-8')
return format_html(
f'<p>{len(notenbild.bild_data)} bytes</p>'
f'<img src="data:image/png;base64,{encoded_image}" style="max-width:40rem; max-height:16rem" />'
)
, который отлично работает для сохранения изображений через интерфейс администратора, которые соответствуют ограничениям по размеру. Однако при попытке сохранить файл, размер которого превышает установленный лимит, я получаю очень странную ошибку:
KeyError at /admin/library/notenbild/368/change/
"Your dictionary lacks key 'max'. Please provide it, because it is required to determine whether string is singular or plural."
Django Version: 5.1.1
Exception Location: /Users/alex/Repositories/ekd-cms/venv/lib/python3.12/site-packages/django/utils/translation/__init__.py, line 130, in _get_number_value
, при этом полный трассировщик стека будет
Traceback (most recent call last):
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/utils/translation/__init__.py", line 128, in _get_number_value
return values[number]
^^^^^^^^^^^^^^
During handling of the above exception ('max'), another exception occurred:
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/debug_toolbar/middleware.py", line 92, in __call__
panel.generate_stats(request, response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/debug_toolbar/panels/templates/panel.py", line 201, in generate_stats
template_data["context_list"] = self.process_context_list(
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/debug_toolbar/panels/templates/panel.py", line 134, in process_context_list
if key_values == context_layer:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/forms/utils.py", line 192, in __eq__
return list(self) == other
^^^^^^^^^^^^^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/forms/utils.py", line 192, in __eq__
return list(self) == other
^^^^^^^^^^
File "<frozen _collections_abc>", line 1026, in __iter__
<source code not available>
^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/forms/utils.py", line 197, in __getitem__
return next(iter(error))
^^^^^^^^^^^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/core/exceptions.py", line 210, in __iter__
message %= error.params
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/utils/functional.py", line 167, in __mod__
return self.__cast() % other
^^^^^^^^^^^^^^^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/utils/translation/__init__.py", line 148, in __mod__
number_value = self._get_number_value(rhs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/alex/Repositories/my-project/venv/lib/python3.12/site-packages/django/utils/translation/__init__.py", line 130, in _get_number_value
raise KeyError(
^
, что не имеет для меня никакого смысла. Валидация правильно отловила недопустимые данные, но, похоже, это может быть ошибкой Django? Или я неправильно использую эту штуку?
Я пробовал добавить свой собственный валидатор к модели:
from django.core.validators import BaseValidator
from django.utils.deconstruct import deconstructible
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
@deconstructible
class MaxFileSizeValidator(BaseValidator):
message = _("Ensure the file is less than or equal to %(limit_value)s.")
code = "limit_value"
def __init__(self, limit_value, message=None):
super().__init__(limit_value, message)
self.limit_value = limit_value
if message:
self.message = message
def __call__(self, value):
cleaned = self.clean(value)
limit_value = (
self.limit_value() if callable(self.limit_value) else self.limit_value
)
params = {"limit_value": limit_value, "show_value": cleaned, "value": value}
if self.compare(cleaned, limit_value):
raise ValidationError(self.message, code=self.code, params=params)
def compare(self, a, b):
return len(a) > b
и
class Notenbild(models.Model):
bild_data = models.BinaryField(max_length=500000, editable=True, validators=[MaxFileSizeValidator(500000)])
но как бы я ни пытался, метод перевода __init__.py
продолжает выдавать эту ошибку, которая не имеет для меня большого смысла. Есть идеи?