在Django管理表单中将MultiValueField和MultiWidget用于一定范围

时间:2018-11-02 23:15:12

标签: python-3.7 django-2.1

首先,我在Windows和Django 2.1.3上使用Python 3.7。

我正在尝试在Django表的CharField中存储一系列数字。我还希望管理站点并排显示两个数字文本框,分别表示下限值和上限值。我有以下文件和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()方法中所做的验证,在该方法中,我确定下限不大于上限。

1 个答案:

答案 0 :(得分:0)

msrp = RangeField()添加到CarChangeForm解决了该问题。 Django不会自动为您的列初始化字段。您必须自己填写表格。