Как мне добавить пользовательскую проверку в админку Django для нестандартного поля модели?

У меня есть нестандартное поле модели Django (PostgreSQL pgvector), что приводит к ошибке проверки, когда приложение администратора пытается его проверить:

Истинное значение массива, содержащего более одного элемента, неоднозначно. Используйте a.any() или a.all().

Я безуспешно пытался указать функцию проверки, специфичную для вектора, используя список валидаторов поля модели, переопределяя чистый метод модели и т. д.

Каким будет правильный способ справиться с этим?

Обратная трассировка:

      Traceback (most recent call last):
    File "/home/appuser/.local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 688, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/utils/decorators.py", line 134, in _wrapper_view
    response = view_func(request, *args, **kwargs)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/views/decorators/cache.py", line 62, in _wrapper_view_func
    response = view_func(request, *args, **kwargs)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 242, in inner
    return view(request, *args, **kwargs)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1886, in add_view
    return self.changeform_view(request, None, form_url, extra_context)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/utils/decorators.py", line 46, in _wrapper
    return bound_method(*args, **kwargs)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/utils/decorators.py", line 134, in _wrapper_view
    response = view_func(request, *args, **kwargs)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1747, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1792, in _changeform_view
    form_validated = form.is_valid()
    File "/home/appuser/.local/lib/python3.8/site-packages/django/forms/forms.py", line 201, in is_valid
    return self.is_bound and not self.errors
    File "/home/appuser/.local/lib/python3.8/site-packages/django/forms/forms.py", line 196, in errors
    self.full_clean()
    File "/home/appuser/.local/lib/python3.8/site-packages/django/forms/forms.py", line 435, in full_clean
    self._post_clean()
    File "/home/appuser/.local/lib/python3.8/site-packages/django/forms/models.py", line 486, in _post_clean
    self.instance.full_clean(exclude=exclude, validate_unique=False)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/db/models/base.py", line 1470, in full_clean
    self.clean_fields(exclude=exclude)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/db/models/base.py", line 1522, in clean_fields
    setattr(self, f.attname, f.clean(raw_value, self))
    File "/home/appuser/.local/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 777, in clean
    self.validate(value, model_instance)
    File "/home/appuser/.local/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 767, in validate
    if not self.blank and value in self.empty_values:

Exception Type: ValueError at /admin/api/model_with_vector/add/
Exception Value: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

2 ответа

Короткий ответ : Проблема будет исправлена ​​в следующем выпуске.


Чтобы это заработало, вам необходимо создать несколько пользовательских классов.

  • пользовательский виджет
  • пользовательское поле формы
  • пользовательское поле модели
      # widgets.py
from django import forms


class VectorWidget(forms.TextInput):
    def format_value(self, value):
        try:
            value = value.tolist()
        except AttributeError:
            # value could be None
            pass
        return super().format_value(value)
      # forms.py
from django import forms


class VectorFormField(forms.CharField):
    widget = VectorWidget
      # fields.py
from django.db import models
from pgvector.django import VectorField


class CustomVectorField(_VectorField):
    def validate(self, value, model_instance):
        super().validate(value.tolist(), model_instance)

    def run_validators(self, value):
        super().run_validators(value.tolist())

    def formfield(self, **kwargs):
        return super().formfield(form_class=VectorFormField, **kwargs)
      # models.py

class Vector(models.Model):
    test = CustomVectorField(dimensions=3)

    def __str__(self):
        return str(self.test)

Рекомендации

Если использование встроенных методов проверки не работает, вместо этого вы можете добавить проверку вsaveметод модели, например:

модели.py

      from django.core.exceptions import ValidationError

class MyModel(...):
    array_field = models.ArrayField(...)
    ...

    def save(self, *args, **kwargs):
    
        # raise an error and do not save:
        # check the array, in this example just checking if it is empty:
        if not self.array_field:
            raise ValidationError(
                "the `array_field` cannot be empty",
                ...,
            )

        super().save(*args, **kwargs)
Другие вопросы по тегам