【问题标题】:Using MultiValueField and MultiWidget in Django Admin Form for a range在 Django 管理表单中使用 MultiValueField 和 MultiWidget 获取范围
【发布时间】:2018-11-02 23:15:11
【问题描述】:

首先,我在 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() 方法中完成的验证,我确保下限不大于上限。

【问题讨论】:

    标签: python-3.7 django-2.1


    【解决方案1】:

    msrp = RangeField() 添加到 CarChangeForm 解决了这个问题。 Django 不会自动为您的列初始化字段。您必须在表单中自己完成。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-14
      • 2012-05-09
      • 2012-04-18
      • 1970-01-01
      • 2011-02-21
      • 2014-04-23
      • 1970-01-01
      • 2017-01-17
      相关资源
      最近更新 更多