Есть ли в Django хук для изменения значения поля?

На любом типе Django Field, доступен validators, где я могу подключить свой собственный валидатор. Как я понимаю, я не могу изменить значение в валидаторе, он должен возвращать либо None, либо django.core.exceptions.ValidationError.

Я хочу изменить значение самого поля, прежде чем оно будет сохранено. Например, я хочу сохранить числовой код ISO 3166 в виде столбца:

def digit_only(value):
    if not value.isdigit():
        raise ValidationError(
            _("%(value)s contains a non-digit"),
            params={"value": value},
        )

class Country(models.Model):
   # ...
   iso3166_numeric = models.CharField(max_length=3, validators=[digit_only])
   # ...

Пока все хорошо. Теперь, если вводимое значение равно '1', я хочу преобразовать его в '001' перед тем, как значение будет сохранено в таблице. Могу ли я добиться этого без создания нового типа поля и написания пользовательского Field.to_python(), так же просто, как и с помощью валидаторов?

Я все еще учусь, и я еще не дошел до Forms, но я пытаюсь достичь вышеупомянутого при посеве из CSV через команду Djano. Да, я могу изменить значение сразу при чтении из CSV, но я пытаюсь выяснить, есть ли одно единственное место, где я могу это сделать, чтобы мне не пришлось писать это отдельно и для Forms.

Это называется очисткой, и Django не позволяет подключать "очистители" в самом Model, или, по крайней мере, не так просто. Вы можете чистить на уровне обработки ввода с помощью clean_fieldname() на Forms, validate_fieldname() в сериализаторе и т.д.

Вы могли бы переопределить метод .save() [Django-doc], но они не работают, например, для массового создания.

Поэтому я бы посоветовал просто сделать это с помощью пользовательского поля, например:

from django.core.validators import RegexValidator


class Iso3166NumericField(models.CharField):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault('max_length', 3)
        validators = kwargs.setdefault('validators', [])
        validators.append(RegexValidator('^\\d{,3}$'))
        return super().__init__(*args, **kwargs)

    def to_python(self, value):
        if value is not None:
            value = value.zfill(3)
        return super().to_python(value)


class Country(models.Model):
    # …
    iso3166_numeric = Iso3166NumericField()
Вернуться на верх