Использование MultiValueField и MultiWidget в форме администратора Django для диапазона

Во-первых, я использую Python 3.7 в Windows и Django 2.1.3.

Я пытаюсь сохранить диапазон чисел в CharField в таблице django. Я также хочу, чтобы на сайте администратора отображались два текстовых поля с числами рядом, чтобы обозначить нижний предел значения и верхний предел значения соответственно. У меня есть следующие файлы и код django:

car/models.py

from django.db import models
from django.utils.translation import gettext_lazy as _

# Create your models here.


class Car(models.Model):

    class Meta:
        verbose_name = 'car'
        db_table = 'cars'
        verbose_name_plural = 'cars'

    make = models.CharField(_('manufacturer'), max_length=32)
    model = models.CharField(_('model'), max_length=24)
    msrp = models.CharField(_('suggested price'), max_length=11)

car/admin.py

from django.contrib import admin
from django.forms import ModelForm, MultiWidget, MultiValueField, NumberInput, IntegerField
from django.core.validators import MinValueValidator, MaxValueValidator, ValidationError

# Register your models here.
from .models import Car


class RangeWidget(MultiWidget):
    def __init__(self, *args, **kwargs):
        self.widgets = [NumberInput(), NumberInput()]
        super().__init__(self.widgets, *args, **kwargs)

    def decompress(self, value):
        if value:
            return value.split('/')
        return ['', '']

    def value_from_datadict(self, data, files, name):
        datalist = [
            widget.value_from_datadict(
                data, files, '{name}_{i}'.format(name=name, i=i)
            ) for i, widget in enumerate(self.widgets)
        ]
        try:
            v = '/'.join(datalist)
        except ValueError:
            return ''
        else:
            return v


class RangeField(MultiValueField):
    widget = RangeWidget

    def __init__(self, *args, **kwargs):
        error_messages = {
            'incomplete': 'Enter two numbers',
        }
        fields = (
            IntegerField(
                error_messages={
                    'incomplete': 'Enter a number between 1 and 99999.'
                },
                validators=[
                    MinValueValidator(limit_value=1, message='Field cannot be less than 1'),
                    MaxValueValidator(limit_value=999999, message='Field cannot be greater than 99999')
                ],
                required=True
            ),
            IntegerField(
                error_messages={
                    'incomplete': 'Enter a number between 1 and 99999 that is equal or greater than the lower limit.'
                },
                validators=[
                    MinValueValidator(limit_value=1, message='Field cannot be less than 1'),
                    MaxValueValidator(limit_value=999999, message='Field cannot be greater than 99999')
                ],
                required=True
            )
        )

        super().__init__(
            error_messages=error_messages,
            fields=fields,
            require_all_fields=True,
            **kwargs
        )

    def compress(self, data_list):
        return '/'.join(data_list)


class CarChangeForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['msrp'].widget.attrs.update({'class': 'range', 'min': 1, 'max': 99999})

    class Meta:
        model = Car
        fields = ['make', 'model']
        exclude = []
        widgets = {
            'msrp': RangeWidget
        }

    def clean(self):
        cleaned_data = super().clean()
        try:
            lower, higher = cleaned_data.get('msrp').split('-')
        except (AttributeError, KeyError):
            raise ValidationError(message='Provide both msrp values')

        if lower.isdigit() and higher.isdigit() and int(lower) > int(higher):
            self.add_error(
                'msrp',
                'The lower MSRP cannot be more than higher MSRP.'
            )
        else:
            cleaned_data['msrp_lower'] = lower
            cleaned_data['msrp_higher'] = higher
            return cleaned_data


class CarAdmin(admin.ModelAdmin):
    fields = [
        ('make', 'model',),
        'msrp',
    ]

    form = CarChangeForm

    list_display = ('make', 'model', 'msrp')


admin.site.register(Car, CarAdmin)

Кажется, все работает нормально, за исключением того, что валидаторы, которые я указал в RangeField.fields, не проверяют форму, и мне разрешено вводить отрицательные числа и оставлять поля пустыми. Почему django не соблюдает и не запускает валидаторы, которые я добавил в эти поля? Единственная проверка, которая ДЕЙСТВИТЕЛЬНО работает, - это та, которую я сделал в методе custome clean(), где я удостоверяюсь, что нижний предел не превышает верхний предел.


person boymeetscode    schedule 02.11.2018    source источник


Ответы (1)


добавление msrp = RangeField() в CarChangeForm решило проблему. Django не инициализирует автоматически поле для вашего столбца. Вы должны сделать это самостоятельно в форме.

person boymeetscode    schedule 03.11.2018